Пример #1
0
/*
 * Send a command to the server using protocol v1.  Returns true on success,
 * false on failure.
 */
bool
internal_v1_commandv(struct remctl *r, const struct iovec *command,
                     size_t count)
{
    gss_buffer_desc token;
    size_t i;
    char *p;
    OM_uint32 data, major, minor;
    int status;

    /* Allocate room for the total message: argc, {<length><arg>}+. */
    token.length = 4;
    for (i = 0; i < count; i++)
        token.length += 4 + command[i].iov_len;
    token.value = malloc(token.length);
    if (token.value == NULL) {
        internal_set_error(r, "cannot allocate memory: %s", strerror(errno));
        return false;
    }

    /* First, the argument count.  Then, each argument. */
    p = token.value;
    data = htonl(count);
    memcpy(p, &data, 4);
    p += 4;
    for (i = 0; i < count; i++) {
        data = htonl(command[i].iov_len);
        memcpy(p, &data, 4);
        p += 4;
        memcpy(p, command[i].iov_base, command[i].iov_len);
        p += command[i].iov_len;
    }

    /* Send the result. */
    status = token_send_priv(r->fd, r->context, TOKEN_DATA | TOKEN_SEND_MIC,
                             &token, r->timeout, &major, &minor);
    if (status != TOKEN_OK) {
        internal_token_error(r, "sending token", status, major, minor);
        free(token.value);
        return false;
    }
    free(token.value);
    r->ready = true;
    return true;
}
Пример #2
0
/*
 * Open a new connection to a server.  Returns true on success, false on
 * failure.  On failure, sets the error message appropriately.
 */
bool
internal_open(struct remctl *r, const char *host, unsigned short port,
              const char *principal)
{
    int status, flags;
    bool port_fallback = false;
    socket_type fd = INVALID_SOCKET;
    gss_buffer_desc send_tok, recv_tok, *token_ptr;
    gss_buffer_desc empty_token = { 0, (void *) "" };
    gss_name_t name = GSS_C_NO_NAME;
    gss_ctx_id_t gss_context = GSS_C_NO_CONTEXT;
    OM_uint32 major, minor, init_minor, gss_flags;
    static const OM_uint32 wanted_gss_flags
        = (GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG
           | GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG);
    static const OM_uint32 req_gss_flags
        = (GSS_C_MUTUAL_FLAG | GSS_C_CONF_FLAG | GSS_C_INTEG_FLAG);

    /*
     * If port is 0, default to trying the standard port and then falling back
     * on the old port.
     */
    if (port == 0) {
        port = REMCTL_PORT;
        port_fallback = true;
    }

    /* Make the network connection. */
    fd = internal_connect(r, host, port);
    if (fd == INVALID_SOCKET && port_fallback)
        fd = internal_connect(r, host, REMCTL_PORT_OLD);
    if (fd == INVALID_SOCKET)
        goto fail;
    r->fd = fd;

    /* Import the name. */
    if (!internal_import_name(r, host, principal, &name))
        goto fail;

    /*
     * Default to protocol version two, but if some other protocol is already
     * set in the remctl struct, don't override.  This facility is used only
     * for testing currently.
     */
    if (r->protocol == 0)
        r->protocol = 2;

    /* Send the initial negotiation token. */
    status = token_send(fd, TOKEN_NOOP | TOKEN_CONTEXT_NEXT | TOKEN_PROTOCOL,
                        &empty_token, r->timeout);
    if (status != TOKEN_OK) {
        internal_token_error(r, "sending initial token", status, 0, 0);
        goto fail;
    }

    /* Perform the context-establishment loop.
     *
     * On each pass through the loop, token_ptr points to the token to send to
     * the server (or GSS_C_NO_BUFFER on the first pass).  Every generated
     * token is stored in send_tok which is then transmitted to the server;
     * every received token is stored in recv_tok, which token_ptr is then set
     * to, to be processed by the next call to gss_init_sec_context.
     *
     * GSS-API guarantees that send_tok's length will be non-zero if and only
     * if the server is expecting another token from us, and that
     * gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if and only if the
     * server has another token to send us.
     *
     * We start with the assumption that we're going to do protocol v2, but if
     * the server ever drops TOKEN_PROTOCOL from the response, we fall back to
     * v1.
     */
    token_ptr = GSS_C_NO_BUFFER;
    do {
        major = gss_init_sec_context(&init_minor, GSS_C_NO_CREDENTIAL, 
                    &gss_context, name, (const gss_OID) GSS_KRB5_MECHANISM,
                    wanted_gss_flags, 0, NULL, token_ptr, NULL, &send_tok,
                    &gss_flags, NULL);
        if (token_ptr != GSS_C_NO_BUFFER)
            free(recv_tok.value);

        /* If we have anything more to say, send it. */
        if (send_tok.length != 0) {
            flags = TOKEN_CONTEXT;
            if (r->protocol > 1)
                flags |= TOKEN_PROTOCOL;
            status = token_send(fd, flags, &send_tok, r->timeout);
            if (status != TOKEN_OK) {
                internal_token_error(r, "sending token", status, major, minor);
                gss_release_buffer(&minor, &send_tok);
                goto fail;
            }
        }
        gss_release_buffer(&minor, &send_tok);

        /* On error, report the error and abort. */
        if (major != GSS_S_COMPLETE && major != GSS_S_CONTINUE_NEEDED) {
            internal_gssapi_error(r, "initializing context", major,
                                  init_minor);
            goto fail;
        }

        /* If we're still expecting more, retrieve it. */
        if (major == GSS_S_CONTINUE_NEEDED) {
            status = token_recv(fd, &flags, &recv_tok, TOKEN_MAX_LENGTH,
                                r->timeout);
            if (status != TOKEN_OK) {
                internal_token_error(r, "receiving token", status, major,
                                     minor);
                goto fail;
            }
            if (r->protocol > 1 && (flags & TOKEN_PROTOCOL) != TOKEN_PROTOCOL)
                r->protocol = 1;
            token_ptr = &recv_tok;
        }
    } while (major == GSS_S_CONTINUE_NEEDED);

    /*
     * If the flags we get back from the server are bad and we're doing
     * protocol v2, report an error and abort.  This must be done after
     * establishing the context, since Heimdal doesn't report all flags until
     * context negotiation is complete.
     */
    if (r->protocol > 1 && (gss_flags & req_gss_flags) != req_gss_flags) {
        internal_set_error(r, "server did not negotiate acceptable GSS-API"
                           " flags");
        goto fail;
    }

    /* Success.  Set the context in the struct remctl object. */
    r->context = gss_context;
    r->ready = 0;
    gss_release_name(&minor, &name);
    return true;

fail:
    if (fd != INVALID_SOCKET)
        socket_close(fd);
    r->fd = INVALID_SOCKET;
    if (name != GSS_C_NO_NAME)
        gss_release_name(&minor, &name);
    if (gss_context != GSS_C_NO_CONTEXT)
        gss_delete_sec_context(&minor, &gss_context, GSS_C_NO_BUFFER);
    return false;
}
Пример #3
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;
}