/* 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; }
/* Create a new category map from a string that can either be a filename or a brief "inlined" category map, e.g., "NCATS = 3 ; CDS 1-3". Useful for command-line arguments. */ CategoryMap* cm_new_string_or_file(const char *optarg) { int i; char fname[STR_SHORT_LEN]; String *str = str_new_charstr(optarg); FILE *F; CategoryMap *retval = NULL; str_double_trim(str); if (str_starts_with_charstr(str, "NCATS")) { /* replace semicolons with carriage returns */ for (i = 0; i < str->length; i++) if (str->chars[i] == ';') str->chars[i] = '\n'; /* we'll just dump a little tmp file and read it with cm_read */ sprintf(fname, "cm.tmp.%d", getpid()); F = phast_fopen(fname, "w+"); fprintf(F, "%s", str->chars); phast_fclose(F); F = phast_fopen(fname, "r"); retval = cm_read(F); phast_fclose(F); unlink(fname); } else { F = phast_fopen(str->chars, "r"); retval = cm_read(F); phast_fclose(F); } str_free(str); return retval; }
/* Sets conn->state to READING when done. */ static krb5_boolean service_tcp_write(krb5_context context, const krb5_data *realm, struct conn_state *conn, struct select_state *selstate) { ssize_t nwritten; SOCKET_WRITEV_TEMP tmp; TRACE_SENDTO_KDC_TCP_SEND(context, &conn->addr); nwritten = SOCKET_WRITEV(conn->fd, conn->out.sgp, conn->out.sg_count, tmp); if (nwritten < 0) { TRACE_SENDTO_KDC_TCP_ERROR_SEND(context, &conn->addr, SOCKET_ERRNO); kill_conn(context, conn, selstate); return FALSE; } while (nwritten) { sg_buf *sgp = conn->out.sgp; if ((size_t)nwritten < SG_LEN(sgp)) { SG_ADVANCE(sgp, (size_t)nwritten); nwritten = 0; } else { nwritten -= SG_LEN(sgp); conn->out.sgp++; conn->out.sg_count--; } } if (conn->out.sg_count == 0) { /* Done writing, switch to reading. */ cm_read(selstate, conn->fd); conn->state = READING; } return FALSE; }
/* simple wrapper for cm_read that opens specified filename and reads a category map from it; aborts with error message if unable to open the file. Saves typing in mains for command-line programs */ CategoryMap* cm_read_from_fname(char *fname) { CategoryMap *cm = NULL; FILE *F; F = phast_fopen(fname, "r"); if ((cm = cm_read(F)) == NULL) die("ERROR: cannot read category map from %s.\n", fname); phast_fclose(F); return cm; }
/* Return true on finished data. Call a cm_read/write function and return * false if the TLS layer needs it. Kill the connection on error. */ static krb5_boolean https_read_bytes(krb5_context context, struct conn_state *conn, struct select_state *selstate) { size_t bufsize, nread; k5_tls_status st; char *tmp; struct incoming_message *in = &conn->in; for (;;) { if (in->buf == NULL || in->bufsize - in->pos < 1024) { bufsize = in->bufsize ? in->bufsize * 2 : 8192; if (bufsize > 1024 * 1024) { kill_conn(context, conn, selstate); return FALSE; } tmp = realloc(in->buf, bufsize); if (tmp == NULL) { kill_conn(context, conn, selstate); return FALSE; } in->buf = tmp; in->bufsize = bufsize; } st = context->tls->read(context, conn->http.tls, &in->buf[in->pos], in->bufsize - in->pos - 1, &nread); if (st != DATA_READ) break; in->pos += nread; in->buf[in->pos] = '\0'; } if (st == DONE) return TRUE; 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_RECV(context, &conn->addr); kill_conn(context, conn, selstate); } return FALSE; }
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; }