/* Eingehende Nachricht vom Client verarbeiten */ void server_handle_message(netcp_message *m) { switch (m->type) { case SET_BUFFER: if (debuglevel > 1) printf("> Buffersize: %lu\n", m->data.buffersize); buffersize = m->data.buffersize; server_send_ok(); break; case TERMINATE: if (debuglevel > 1) printf("> Terminate\n"); server_send_ok(); server_terminate(); break; case CLOSE: if (debuglevel > 1) printf("> Close\n"); server_close(); break; case FILENAME: if (debuglevel > 1) printf("> Filename: %s\n", m->data.filename); if (server_open_target(m->data.filename)) { server_send_ok(); } else { server_send_error(); } break; case BEGIN_DATA: if (debuglevel > 1) printf("> Begin data\n"); server_send_ok(); break; case END_DATA: if (debuglevel > 1) printf("> End data\n"); server_close_target(); server_send_ok(); break; case DATA: if (debuglevel > 2) printf("> data\n"); server_read_data(); server_send_ok(); break; case GET: if (debuglevel > 1) printf("> copy to client\n"); server_copy_to_client(); break; case GET_BUFFER: if (debuglevel > 1) printf("> send_buffer\n"); server_send_buffer(); break; default: server_send_ok(); break; } }
/* * Takes the client struct and the server configuration and handles a client * request. Reads a command from the client, checks the ACL, runs the command * if appropriate, and sends any output back to the client. */ void server_v1_handle_messages(struct client *client, struct config *config) { gss_buffer_desc token; OM_uint32 major, minor; struct iovec **argv = NULL; int status, flags; /* Receive the message. */ status = token_recv_priv(client->fd, client->context, &flags, &token, TOKEN_MAX_LENGTH, TIMEOUT, &major, &minor); if (status != TOKEN_OK) { warn_token("receiving command token", status, major, minor); if (status == TOKEN_FAIL_LARGE) server_send_error(client, ERROR_TOOMUCH_DATA, "Too much data"); else if (status != TOKEN_FAIL_EOF) server_send_error(client, ERROR_BAD_TOKEN, "Invalid token"); return; } /* Check the data size. */ if (token.length > TOKEN_MAX_DATA) { warn("command data length %lu exceeds 64KB", (unsigned long) token.length); server_send_error(client, ERROR_TOOMUCH_DATA, "Too much data"); gss_release_buffer(&minor, &token); return; } /* * Do the shared parsing of the message. This code is identical to the * code for v2 (v2 just pulls more data off the front of the token first). */ argv = server_parse_command(client, token.value, token.length); gss_release_buffer(&minor, &token); if (argv == NULL) return; /* * Check the ACL and existence of the command, run the command if * possible, and accumulate the output in the client struct. */ server_run_command(client, config, argv); server_free_command(argv); }
/* * Find the summary of all commands the user can run against this remctl * server. We do so by checking all configuration lines for any that * provide a summary setup that the user can access, then running that * line's command with the given summary sub-command. * * Takes a client object, the user requesting access, and the list of all * valid configurations. */ static void server_send_summary(struct client *client, struct config *config) { char *path = NULL; char *program; struct rule *rule = NULL; size_t i; char **req_argv = NULL; bool ok_any = false; int status_all = 0; struct process process; struct evbuffer *output = NULL; /* Create a buffer to hold all the output for protocol version one. */ if (client->protocol == 1) { output = evbuffer_new(); if (output == NULL) die("internal error: cannot create output buffer"); } /* * Check each line in the config to find any that are "<command> ALL" * lines, the user is authorized to run, and which have a summary field * given. */ for (i = 0; i < config->count; i++) { memset(&process, 0, sizeof(process)); process.client = client; rule = config->rules[i]; if (strcmp(rule->subcommand, "ALL") != 0) continue; if (!server_config_acl_permit(rule, client->user)) continue; if (rule->summary == NULL) continue; ok_any = true; /* * Get the real program name, and use it as the first argument in * argv passed to the command. Then add the summary command to the * argv and pass off to be executed. */ path = rule->program; req_argv = xmalloc(3 * sizeof(char *)); program = strrchr(path, '/'); if (program == NULL) program = path; else program++; req_argv[0] = program; req_argv[1] = rule->summary; req_argv[2] = NULL; process.command = rule->summary; process.argv = req_argv; process.rule = rule; if (server_process_run(&process)) { if (client->protocol == 1) if (evbuffer_add_buffer(output, process.output) < 0) die("internal error: cannot copy data from output buffer"); if (process.status != 0) status_all = process.status; } free(req_argv); } /* * Sets the last process status to 0 if all succeeded, or the last failed * exit status if any commands gave non-zero. Return that we had output * successfully if any command gave it. */ if (WIFEXITED(status_all)) status_all = (int) WEXITSTATUS(process.status); else status_all = -1; if (ok_any) { if (client->protocol == 1) server_v1_send_output(client, output, status_all); else server_v2_send_status(client, status_all); } else { notice("summary request from user %s, but no defined summaries", client->user); server_send_error(client, ERROR_UNKNOWN_COMMAND, "Unknown command"); } if (output != NULL) evbuffer_free(output); }
/* * Process an incoming command. Check the configuration files and the ACL * file, and if appropriate, forks off the command. Takes the argument vector * and the user principal, and a buffer into which to put the output from the * executable or any error message. Returns 0 on success and a negative * integer on failure. * * Using the command and the subcommand, the following argument, a lookup in * the conf data structure is done to find the command executable and acl * file. If the conf file, and subsequently the conf data structure contains * an entry for this command with subcommand equal to "ALL", that is a * wildcard match for any given subcommand. The first argument is then * replaced with the actual program name to be executed. * * After checking the acl permissions, the process forks and the child execv's * the command with pipes arranged to gather output. The parent waits for the * return code and gathers stdout and stderr pipes. */ void server_run_command(struct client *client, struct config *config, struct iovec **argv) { char *command = NULL; char *subcommand = NULL; char *helpsubcommand = NULL; struct rule *rule = NULL; char **req_argv = NULL; size_t i; bool ok = false; bool help = false; const char *user = client->user; struct process process; /* Start with an empty process. */ memset(&process, 0, sizeof(process)); process.client = client; /* * We need at least one argument. This is also rejected earlier when * parsing the command and checking argc, but may as well be sure. */ if (argv[0] == NULL) { notice("empty command from user %s", user); server_send_error(client, ERROR_BAD_COMMAND, "Invalid command token"); goto done; } /* Neither the command nor the subcommand may ever contain nuls. */ for (i = 0; argv[i] != NULL && i < 2; i++) { if (memchr(argv[i]->iov_base, '\0', argv[i]->iov_len)) { notice("%s from user %s contains nul octet", (i == 0) ? "command" : "subcommand", user); server_send_error(client, ERROR_BAD_COMMAND, "Invalid command token"); goto done; } } /* We need the command and subcommand as nul-terminated strings. */ command = xstrndup(argv[0]->iov_base, argv[0]->iov_len); if (argv[1] != NULL) subcommand = xstrndup(argv[1]->iov_base, argv[1]->iov_len); /* * Find the program path we need to run. If we find no matching command * at first and the command is a help command, then we either dispatch * to the summary command if no specific help was requested, or if a * specific help command was listed, check for that in the configuration * instead. */ rule = find_config_line(config, command, subcommand); if (rule == NULL && strcmp(command, "help") == 0) { /* Error if we have more than a command and possible subcommand. */ if (argv[1] != NULL && argv[2] != NULL && argv[3] != NULL) { notice("help command from user %s has more than three arguments", user); server_send_error(client, ERROR_TOOMANY_ARGS, "Too many arguments for help command"); } if (subcommand == NULL) { server_send_summary(client, config); goto done; } else { help = true; if (argv[2] != NULL) helpsubcommand = xstrndup(argv[2]->iov_base, argv[2]->iov_len); rule = find_config_line(config, subcommand, helpsubcommand); } } /* * Arguments may only contain nuls if they're the argument being passed on * standard input. */ for (i = 1; argv[i] != NULL; i++) { if (rule != NULL) { if (help == false && (long) i == rule->stdin_arg) continue; if (argv[i + 1] == NULL && rule->stdin_arg == -1) continue; } if (memchr(argv[i]->iov_base, '\0', argv[i]->iov_len)) { notice("argument %lu from user %s contains nul octet", (unsigned long) i, user); server_send_error(client, ERROR_BAD_COMMAND, "Invalid command token"); goto done; } } /* Log after we look for command so we can get potentially get logmask. */ server_log_command(argv, rule, user); /* * Check the command, aclfile, and the authorization of this client to * run this command. */ if (rule == NULL) { notice("unknown command %s%s%s from user %s", command, (subcommand == NULL) ? "" : " ", (subcommand == NULL) ? "" : subcommand, user); server_send_error(client, ERROR_UNKNOWN_COMMAND, "Unknown command"); goto done; } if (!server_config_acl_permit(rule, user)) { notice("access denied: user %s, command %s%s%s", user, command, (subcommand == NULL) ? "" : " ", (subcommand == NULL) ? "" : subcommand); server_send_error(client, ERROR_ACCESS, "Access denied"); goto done; } /* * Check for a specific command help request with the rule and do error * checking and arg massaging. */ if (help) { if (rule->help == NULL) { notice("command %s from user %s has no defined help", command, user); server_send_error(client, ERROR_NO_HELP, "No help defined for command"); goto done; } else { free(subcommand); subcommand = xstrdup(rule->help); } } /* Assemble the argv for the command we're about to run. */ if (help) req_argv = create_argv_help(rule->program, subcommand, helpsubcommand); else req_argv = create_argv_command(rule, &process, argv); /* Now actually execute the program. */ process.command = command; process.argv = req_argv; process.rule = rule; ok = server_process_run(&process); if (ok) { if (WIFEXITED(process.status)) process.status = (signed int) WEXITSTATUS(process.status); else process.status = -1; if (client->protocol == 1) server_v1_send_output(client, process.output, process.status); else server_v2_send_status(client, process.status); } done: free(command); free(subcommand); free(helpsubcommand); if (req_argv != NULL) { for (i = 0; req_argv[i] != NULL; i++) free(req_argv[i]); free(req_argv); } if (process.input != NULL) evbuffer_free(process.input); if (process.output != NULL) evbuffer_free(process.output); }