/* * 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); }
int main(void) { struct kerberos_config *config; struct remctl *r; OM_uint32 major, minor; int flags, status; gss_buffer_desc tok; /* Unless we have Kerberos available, we can't really do anything. */ config = kerberos_setup(TAP_KRB_NEEDS_KEYTAB); remctld_start(config, "data/conf-simple", NULL); plan(7); /* Open the connection to the site. */ r = remctl_new(); ok(r != NULL, "remctl_new"); ok(remctl_open(r, "localhost", 14373, config->principal), "remctl_open"); /* Send the no-op token. */ tok.length = sizeof(token); tok.value = (char *) token; status = token_send_priv(r->fd, r->context, TOKEN_DATA | TOKEN_PROTOCOL, &tok, 0, &major, &minor); if (status != TOKEN_OK) bail("cannot send token"); /* Accept the remote token. */ status = token_recv_priv(r->fd, r->context, &flags, &tok, 1024 * 64, 0, &major, &minor); is_int(TOKEN_OK, status, "received token correctly"); is_int(TOKEN_DATA | TOKEN_PROTOCOL, flags, "token had correct flags"); is_int(2, tok.length, "token had correct length"); is_int(3, ((char *) tok.value)[0], "protocol version is 3"); is_int(MESSAGE_NOOP, ((char *) tok.value)[1], "no-op message"); /* Close things out. */ remctl_close(r); return 0; }
/* * Retrieve the output from the server using protocol version one and return * it. This function will actually be called twice, once to retrieve the * output data and once to retrieve the exit status. The old protocol * returned those together in one message, so we have to buffer the exit * status and return it on the second call. Returns a remctl output struct on * success and NULL on failure. */ struct remctl_output * internal_v1_output(struct remctl *r) { int status, flags; gss_buffer_desc token; OM_uint32 data, major, minor, length; char *p; /* * First, see if we already had an output struct. If so, this is the * second call and we should just return the exit status. */ if (r->output != NULL && !r->ready) { if (r->output->type == REMCTL_OUT_STATUS) r->output->type = REMCTL_OUT_DONE; else { internal_output_wipe(r->output); r->output->type = REMCTL_OUT_STATUS; } r->output->status = r->status; return r->output; } /* Otherwise, we have to read the token from the server. */ status = token_recv_priv(r->fd, r->context, &flags, &token, TOKEN_MAX_LENGTH, r->timeout, &major, &minor); if (status != TOKEN_OK) { internal_token_error(r, "receiving token", status, major, minor); if (status == TOKEN_FAIL_EOF || status == TOKEN_FAIL_TIMEOUT) { gss_delete_sec_context(&minor, &r->context, GSS_C_NO_BUFFER); r->context = GSS_C_NO_CONTEXT; socket_close(r->fd); r->fd = INVALID_SOCKET; r->ready = false; } return NULL; } if (flags != TOKEN_DATA) { internal_set_error(r, "unexpected token from server"); gss_release_buffer(&minor, &token); return NULL; } /* Extract the return code, message length, and data. */ if (token.length < 8) { internal_set_error(r, "malformed result token from server"); gss_release_buffer(&minor, &token); return NULL; } p = token.value; memcpy(&data, p, 4); r->status = ntohl(data); p += 4; memcpy(&data, p, 4); length = ntohl(data); p += 4; if (length != token.length - 8) { internal_set_error(r, "malformed result token from server"); gss_release_buffer(&minor, &token); return NULL; } /* * Allocate the new output token. We make another copy of the data, * unfortunately, so that we don't have to keep the token around to free * later. */ r->output = malloc(sizeof(struct remctl_output)); if (r->output == NULL) { internal_set_error(r, "cannot allocate memory: %s", strerror(errno)); gss_release_buffer(&minor, &token); return NULL; } r->output->type = REMCTL_OUT_OUTPUT; r->output->data = malloc(length); if (r->output->data == NULL) { internal_set_error(r, "cannot allocate memory: %s", strerror(errno)); gss_release_buffer(&minor, &token); return NULL; } memcpy(r->output->data, p, length); r->output->length = length; gss_release_buffer(&minor, &token); /* * We always claim everything was stdout since we have no way of knowing * better with protocol version one. */ r->output->stream = 1; /* * We can only do one round with protocol version one, so close the * connection now. */ gss_delete_sec_context(&minor, &r->context, GSS_C_NO_BUFFER); r->context = GSS_C_NO_CONTEXT; socket_close(r->fd); r->fd = INVALID_SOCKET; r->ready = false; return r->output; }