Ejemplo n.º 1
0
/* Set conn->state to READING when done; otherwise, call a cm_set_. */
static krb5_boolean
service_https_write(krb5_context context, const krb5_data *realm,
                    struct conn_state *conn, struct select_state *selstate)
{
    k5_tls_status st;

    /* If this is our first time in here, set up the SSL context. */
    if (conn->http.tls == NULL && !setup_tls(context, realm, conn, selstate)) {
        kill_conn(context, conn, selstate);
        return FALSE;
    }

    /* Try to transmit our request to the server. */
    st = context->tls->write(context, conn->http.tls, SG_BUF(conn->out.sgp),
                             SG_LEN(conn->out.sgbuf));
    if (st == DONE) {
        TRACE_SENDTO_KDC_HTTPS_SEND(context, &conn->addr);
        cm_read(selstate, conn->fd);
        conn->state = READING;
    } else if (st == WANT_READ) {
        cm_read(selstate, conn->fd);
    } else if (st == WANT_WRITE) {
        cm_write(selstate, conn->fd);
    } else if (st == ERROR_TLS) {
        TRACE_SENDTO_KDC_HTTPS_ERROR_SEND(context, &conn->addr);
        kill_conn(context, conn, selstate);
    }

    return FALSE;
}
Ejemplo n.º 2
0
/* Return 0 if we sent something, non-0 otherwise.
   If 0 is returned, the caller should delay waiting for a response.
   Otherwise, the caller should immediately move on to process the
   next connection.  */
static int
maybe_send(krb5_context context, struct conn_state *conn,
           struct select_state *selstate,
           struct sendto_callback_info *callback_info)
{
    sg_buf *sg;
    ssize_t ret;

    dprint("maybe_send(@%p) state=%s type=%s\n", conn,
           state_strings[conn->state],
           conn->is_udp ? "udp" : "tcp");
    if (conn->state == INITIALIZING)
        return start_connection(context, conn, selstate, callback_info);

    /* Did we already shut down this channel?  */
    if (conn->state == FAILED) {
        dprint("connection already closed\n");
        return -1;
    }

    if (conn->socktype == SOCK_STREAM) {
        dprint("skipping stream socket\n");
        /* The select callback will handle flushing any data we
           haven't written yet, and we only write it once.  */
        return -1;
    }

    /* UDP - retransmit after a previous attempt timed out. */
    sg = &conn->x.out.sgbuf[0];
    TRACE_SENDTO_KDC_UDP_SEND_RETRY(context, conn);
    dprint("sending %d bytes on fd %d\n", SG_LEN(sg), conn->fd);
    ret = send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0);
    if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
        TRACE_SENDTO_KDC_UDP_ERROR_SEND_RETRY(context, conn, SOCKET_ERRNO);
        dperror("send");
        /* Keep connection alive, we'll try again next pass.

           Is this likely to catch any errors we didn't get from the
           select callbacks?  */
        return -1;
    }
    /* Yay, it worked.  */
    return 0;
}
Ejemplo n.º 3
0
/* Return 0 if we sent something, non-0 otherwise.
   If 0 is returned, the caller should delay waiting for a response.
   Otherwise, the caller should immediately move on to process the
   next connection.  */
static int
maybe_send(krb5_context context, struct conn_state *conn,
           const krb5_data *message, struct select_state *selstate,
           const krb5_data *realm,
           struct sendto_callback_info *callback_info)
{
    sg_buf *sg;
    ssize_t ret;

    if (conn->state == INITIALIZING) {
        return start_connection(context, conn, message, selstate,
                                realm, callback_info);
    }

    /* Did we already shut down this channel?  */
    if (conn->state == FAILED) {
        return -1;
    }

    if (conn->addr.transport != UDP) {
        /* The select callback will handle flushing any data we
           haven't written yet, and we only write it once.  */
        return -1;
    }

    /* UDP - retransmit after a previous attempt timed out. */
    sg = &conn->out.sgbuf[0];
    TRACE_SENDTO_KDC_UDP_SEND_RETRY(context, &conn->addr);
    ret = send(conn->fd, SG_BUF(sg), SG_LEN(sg), 0);
    if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
        TRACE_SENDTO_KDC_UDP_ERROR_SEND_RETRY(context, &conn->addr,
                                              SOCKET_ERRNO);
        /* Keep connection alive, we'll try again next pass.

           Is this likely to catch any errors we didn't get from the
           select callbacks?  */
        return -1;
    }
    /* Yay, it worked.  */
    return 0;
}
Ejemplo n.º 4
0
static int
start_connection(krb5_context context, struct conn_state *state,
                 const krb5_data *message, struct select_state *selstate,
                 const krb5_data *realm,
                 struct sendto_callback_info *callback_info)
{
    int fd, e, type;
    static const int one = 1;
    static const struct linger lopt = { 0, 0 };

    type = socktype_for_transport(state->addr.transport);
    fd = socket(state->addr.family, type, 0);
    if (fd == INVALID_SOCKET)
        return -1;              /* try other hosts */
    set_cloexec_fd(fd);
    /* Make it non-blocking.  */
    ioctlsocket(fd, FIONBIO, (const void *) &one);
    if (state->addr.transport == TCP) {
        setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt));
        TRACE_SENDTO_KDC_TCP_CONNECT(context, &state->addr);
    }

    /* Start connecting to KDC.  */
    e = connect(fd, (struct sockaddr *)&state->addr.saddr, state->addr.len);
    if (e != 0) {
        /*
         * This is the path that should be followed for non-blocking
         * connections.
         */
        if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
            state->state = CONNECTING;
            state->fd = fd;
        } else {
            (void) closesocket(fd);
            state->state = FAILED;
            return -2;
        }
    } else {
        /*
         * Connect returned zero even though we made it non-blocking.  This
         * happens normally for UDP sockets, and can perhaps also happen for
         * TCP sockets connecting to localhost.
         */
        state->state = WRITING;
        state->fd = fd;
    }

    /*
     * Here's where KPASSWD callback gets the socket information it needs for
     * a kpasswd request
     */
    if (callback_info) {

        e = callback_info->pfn_callback(state->fd, callback_info->data,
                                        &state->callback_buffer);
        if (e != 0) {
            (void) closesocket(fd);
            state->fd = INVALID_SOCKET;
            state->state = FAILED;
            return -3;
        }

        message = &state->callback_buffer;
    }

    e = set_transport_message(state, realm, message);
    if (e != 0) {
        TRACE_SENDTO_KDC_ERROR_SET_MESSAGE(context, &state->addr, e);
        (void) closesocket(state->fd);
        state->fd = INVALID_SOCKET;
        state->state = FAILED;
        return -4;
    }

    if (state->addr.transport == UDP) {
        /* Send it now.  */
        ssize_t ret;
        sg_buf *sg = &state->out.sgbuf[0];

        TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, &state->addr);
        ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
        if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
            TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, &state->addr,
                                                    SOCKET_ERRNO);
            (void) closesocket(state->fd);
            state->fd = INVALID_SOCKET;
            state->state = FAILED;
            return -5;
        } else {
            state->state = READING;
        }
    }

    if (!cm_add_fd(selstate, state->fd)) {
        (void) closesocket(state->fd);
        state->fd = INVALID_SOCKET;
        state->state = FAILED;
        return -1;
    }
    if (state->state == CONNECTING || state->state == WRITING)
        cm_write(selstate, state->fd);
    else
        cm_read(selstate, state->fd);

    return 0;
}
Ejemplo n.º 5
0
static int
start_connection(krb5_context context, struct conn_state *state,
                 struct select_state *selstate,
                 struct sendto_callback_info *callback_info)
{
    int fd, e;

    dprint("start_connection(@%p)\ngetting %s socket in family %d...", state,
           state->socktype == SOCK_STREAM ? "stream" : "dgram", state->family);
    fd = socket(state->family, state->socktype, 0);
    if (fd == INVALID_SOCKET) {
        state->err = SOCKET_ERRNO;
        dprint("socket: %m creating with af %d\n", state->err, state->family);
        return -1;              /* try other hosts */
    }
#ifndef _WIN32 /* On Windows FD_SETSIZE is a count, not a max value.  */
    if (fd >= FD_SETSIZE) {
        closesocket(fd);
        state->err = EMFILE;
        dprint("socket: fd %d too high\n", fd);
        return -1;
    }
#endif
    set_cloexec_fd(fd);
    /* Make it non-blocking.  */
    if (state->socktype == SOCK_STREAM) {
        static const int one = 1;
        static const struct linger lopt = { 0, 0 };

        if (ioctlsocket(fd, FIONBIO, (const void *) &one))
            dperror("sendto_kdc: ioctl(FIONBIO)");
        if (setsockopt(fd, SOL_SOCKET, SO_LINGER, &lopt, sizeof(lopt)))
            dperror("sendto_kdc: setsockopt(SO_LINGER)");
        TRACE_SENDTO_KDC_TCP_CONNECT(context, state);
    }

    /* Start connecting to KDC.  */
    e = connect(fd, (struct sockaddr *)&state->addr, state->addrlen);
    if (e != 0) {
        /*
         * This is the path that should be followed for non-blocking
         * connections.
         */
        if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
            state->state = CONNECTING;
            state->fd = fd;
        } else {
            dprint("connect failed: %m\n", SOCKET_ERRNO);
            (void) closesocket(fd);
            state->err = SOCKET_ERRNO;
            state->state = FAILED;
            return -2;
        }
    } else {
        /*
         * Connect returned zero even though we made it non-blocking.  This
         * happens normally for UDP sockets, and can perhaps also happen for
         * TCP sockets connecting to localhost.
         */
        state->state = WRITING;
        state->fd = fd;
    }
    dprint("new state = %s\n", state_strings[state->state]);


    /*
     * Here's where KPASSWD callback gets the socket information it needs for
     * a kpasswd request
     */
    if (callback_info) {

        e = callback_info->pfn_callback(state, callback_info->context,
                                        &state->callback_buffer);
        if (e != 0) {
            dprint("callback failed: %m\n", e);
            (void) closesocket(fd);
            state->err = e;
            state->fd = INVALID_SOCKET;
            state->state = FAILED;
            return -3;
        }

        set_conn_state_msg_length(state, &state->callback_buffer);
    }

    if (state->socktype == SOCK_DGRAM) {
        /* Send it now.  */
        ssize_t ret;
        sg_buf *sg = &state->x.out.sgbuf[0];

        TRACE_SENDTO_KDC_UDP_SEND_INITIAL(context, state);
        dprint("sending %d bytes on fd %d\n", SG_LEN(sg), state->fd);
        ret = send(state->fd, SG_BUF(sg), SG_LEN(sg), 0);
        if (ret < 0 || (size_t) ret != SG_LEN(sg)) {
            TRACE_SENDTO_KDC_UDP_ERROR_SEND_INITIAL(context, state,
                                                    SOCKET_ERRNO);
            dperror("sendto");
            (void) closesocket(state->fd);
            state->fd = INVALID_SOCKET;
            state->state = FAILED;
            return -4;
        } else {
            state->state = READING;
        }
    }
    FD_SET(state->fd, &selstate->rfds);
    if (state->state == CONNECTING || state->state == WRITING)
        FD_SET(state->fd, &selstate->wfds);
    FD_SET(state->fd, &selstate->xfds);
    if (selstate->max <= state->fd)
        selstate->max = state->fd + 1;
    selstate->nfds++;

    dprint("new select vectors: %F\n",
           &selstate->rfds, &selstate->wfds, &selstate->xfds, selstate->max);

    return 0;
}