/*
* krb5_set_password - Implements set password per RFC 3244
* Added by Paul W. Nelson, Thursby Software Systems, Inc.
+ * Modified by Todd Stecher, Isilon Systems, to use krb1.4 socket infrastructure
*/
#include "fake-addrinfo.h"
#include "k5-int.h"
#include "os-proto.h"
+#include "cm.h"
#include <stdio.h>
#include <errno.h>
#define GETSOCKNAME_ARG3_TYPE int
#endif
+struct sendto_callback_context {
+ krb5_context context;
+ krb5_auth_context auth_context;
+ krb5_principal set_password_for;
+ char *newpw;
+ krb5_data ap_req;
+};
+
+
/*
* Wrapper function for the two backends
*/
static krb5_error_code
krb5_locate_kpasswd(krb5_context context, const krb5_data *realm,
- struct addrlist *addrlist)
+ struct addrlist *addrlist, krb5_boolean useTcp)
{
krb5_error_code code;
+ int sockType = (useTcp ? SOCK_STREAM : SOCK_DGRAM);
code = krb5int_locate_server (context, realm, addrlist,
- locate_service_kpasswd, 0, 0);
+ locate_service_kpasswd, sockType, 0);
+
if (code == KRB5_REALM_CANT_RESOLVE || code == KRB5_REALM_UNKNOWN) {
code = krb5int_locate_server (context, realm, addrlist,
- locate_service_kadmin, 1, 0);
+ locate_service_kadmin, SOCK_STREAM, 0);
if (!code) {
/* Success with admin_server but now we need to change the
port number to use DEFAULT_KPASSWD_PORT. */
int i;
- for ( i=0;i<addrlist->naddrs;i++ ) {
+ for (i=0; i<addrlist->naddrs; i++) {
struct addrinfo *a = addrlist->addrs[i].ai;
if (a->ai_family == AF_INET)
sa2sin (a->ai_addr)->sin_port = htons(DEFAULT_KPASSWD_PORT);
}
-/*
-** The logic for setting and changing a password is mostly the same
-** krb5_change_set_password handles both cases
-** if set_password_for is NULL, then a password change is performed,
-** otherwise, the password is set for the principal indicated in set_password_for
-*/
-krb5_error_code KRB5_CALLCONV
-krb5_change_set_password(
- krb5_context context, krb5_creds *creds, char *newpw, krb5_principal set_password_for,
- int *result_code, krb5_data *result_code_string, krb5_data *result_string)
+/**
+ * This routine is used for a callback in sendto_kdc.c code. Simply
+ * put, we need the client addr to build the krb_priv portion of the
+ * password request.
+ */
+
+
+static void kpasswd_sendto_msg_cleanup (void* callback_context, krb5_data* message)
{
- krb5_auth_context auth_context;
- krb5_data ap_req, chpw_req, chpw_rep;
- krb5_address local_kaddr, remote_kaddr;
- char *code_string;
- krb5_error_code code = 0;
- int i;
- GETSOCKNAME_ARG3_TYPE addrlen;
- struct sockaddr_storage local_addr, remote_addr, tmp_addr;
- int cc, local_result_code;
- /* platforms seem to be consistant and use the same types */
- GETSOCKNAME_ARG3_TYPE tmp_len;
- SOCKET s1 = INVALID_SOCKET, s2 = INVALID_SOCKET;
- int tried_one = 0;
- struct addrlist al = ADDRLIST_INIT;
-
-
- /* Initialize values so that cleanup call can safely check for NULL */
- auth_context = NULL;
- memset(&chpw_req, 0, sizeof(krb5_data));
- memset(&chpw_rep, 0, sizeof(krb5_data));
- memset(&ap_req, 0, sizeof(krb5_data));
-
- /* initialize auth_context so that we know we have to free it */
- if ((code = krb5_auth_con_init(context, &auth_context)))
- goto cleanup;
-
- if ((code = krb5_mk_req_extended(context, &auth_context,
- AP_OPTS_USE_SUBKEY,
- NULL, creds, &ap_req)))
- goto cleanup;
-
- if ((code = krb5_locate_kpasswd(context,
- krb5_princ_realm(context, creds->server),
- &al)))
- goto cleanup;
-
- /* this is really obscure. s1 is used for all communications. it
- is left unconnected in case the server is multihomed and routes
- are asymmetric. s2 is connected to resolve routes and get
- addresses. this is the *only* way to get proper addresses for
- multihomed hosts if routing is asymmetric.
-
- A related problem in the server, but not the client, is that
- many os's have no way to disconnect a connected udp socket, so
- the s2 socket needs to be closed and recreated for each
- request. The s1 socket must not be closed, or else queued
- requests will be lost.
-
- A "naive" client implementation (one socket, no connect,
- hostname resolution to get the local ip addr) will work and
- interoperate if the client is single-homed. */
-
- if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
- code = SOCKET_ERRNO;
- goto cleanup;
- }
+ struct sendto_callback_context *ctx = callback_context;
+ krb5_free_data_contents(ctx->context, message);
+}
+
+
+static int kpasswd_sendto_msg_callback(struct conn_state *conn, void *callback_context, krb5_data* message)
+{
+ krb5_error_code code = 0;
+ struct sockaddr_storage local_addr;
+ krb5_address local_kaddr;
+ struct sendto_callback_context *ctx = callback_context;
+ GETSOCKNAME_ARG3_TYPE addrlen;
+ krb5_data output;
- if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
+ memset (message, 0, sizeof(krb5_data));
+
+ /*
+ * We need the local addr from the connection socket
+ */
+ addrlen = sizeof(local_addr);
+
+ if (getsockname(conn->fd, ss2sa(&local_addr), &addrlen) < 0) {
code = SOCKET_ERRNO;
goto cleanup;
}
- /*
- * This really should try fallback addresses in cases of timeouts.
- * For now, where the MIT KDC implementation only supports one
- * kpasswd server machine anyways, we'll only try the first IPv4
- * address we can connect() to. This isn't right for multi-homed
- * servers; oh well.
- */
- for (i=0; i<al.naddrs; i++) {
- fd_set fdset;
- struct timeval timeout;
+ /* some brain-dead OS's don't return useful information from
+ * the getsockname call. Namely, windows and solaris. */
- /* XXX Now the locate_ functions can return IPv6 addresses. */
- if (al.addrs[i].ai->ai_family != AF_INET)
- continue;
+ if (ss2sin(&local_addr)->sin_addr.s_addr != 0) {
+ local_kaddr.addrtype = ADDRTYPE_INET;
+ local_kaddr.length = sizeof(ss2sin(&local_addr)->sin_addr);
+ local_kaddr.contents = (krb5_octet *) &ss2sin(&local_addr)->sin_addr;
+ } else {
+ krb5_address **addrs;
- tried_one = 1;
- if (connect(s2, al.addrs[i].ai->ai_addr, al.addrs[i].ai->ai_addrlen) == SOCKET_ERROR) {
- if (SOCKET_ERRNO == ECONNREFUSED || SOCKET_ERRNO == EHOSTUNREACH)
- continue; /* try the next addr */
+ code = krb5_os_localaddr(ctx->context, &addrs);
+ if (code)
+ goto cleanup;
- code = SOCKET_ERRNO;
+ local_kaddr.magic = addrs[0]->magic;
+ local_kaddr.addrtype = addrs[0]->addrtype;
+ local_kaddr.length = addrs[0]->length;
+ local_kaddr.contents = malloc(addrs[0]->length);
+ if (local_kaddr.contents == NULL && addrs[0]->length != 0) {
+ code = errno;
+ krb5_free_addresses(ctx->context, addrs);
goto cleanup;
}
+ memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length);
- addrlen = sizeof(local_addr);
+ krb5_free_addresses(ctx->context, addrs);
+ }
- if (getsockname(s2, ss2sa(&local_addr), &addrlen) < 0) {
- if (SOCKET_ERRNO == ECONNREFUSED || SOCKET_ERRNO == EHOSTUNREACH)
- continue; /* try the next addr */
- code = SOCKET_ERRNO;
- goto cleanup;
- }
+ /*
+ * TBD: Does this tamper w/ the auth context in such a way
+ * to break us? Yes - provide 1 per conn-state / host...
+ */
- /* some brain-dead OS's don't return useful information from
- * the getsockname call. Namely, windows and solaris. */
- if (ss2sin(&local_addr)->sin_addr.s_addr != 0) {
- local_kaddr.addrtype = ADDRTYPE_INET;
- local_kaddr.length = sizeof(ss2sin(&local_addr)->sin_addr);
- local_kaddr.contents = (krb5_octet *) &ss2sin(&local_addr)->sin_addr;
- } else {
- krb5_address **addrs;
+ if ((code = krb5_auth_con_setaddrs(ctx->context, ctx->auth_context,
+ &local_kaddr, NULL)))
+ goto cleanup;
- krb5_os_localaddr(context, &addrs);
+ if (ctx->set_password_for)
+ code = krb5int_mk_setpw_req(ctx->context,
+ ctx->auth_context,
+ &ctx->ap_req,
+ ctx->set_password_for,
+ ctx->newpw,
+ &output);
+ else
+ code = krb5int_mk_chpw_req(ctx->context,
+ ctx->auth_context,
+ &ctx->ap_req,
+ ctx->newpw,
+ &output);
+ if (code)
+ goto cleanup;
- local_kaddr.magic = addrs[0]->magic;
- local_kaddr.addrtype = addrs[0]->addrtype;
- local_kaddr.length = addrs[0]->length;
- local_kaddr.contents = malloc(addrs[0]->length);
- memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length);
+ message->length = output.length;
+ message->data = output.data;
- krb5_free_addresses(context, addrs);
- }
+cleanup:
+ return code;
+}
+
+
+/*
+** The logic for setting and changing a password is mostly the same
+** krb5_change_set_password handles both cases
+** if set_password_for is NULL, then a password change is performed,
+** otherwise, the password is set for the principal indicated in set_password_for
+*/
+krb5_error_code KRB5_CALLCONV
+krb5_change_set_password(krb5_context context, krb5_creds *creds, char *newpw,
+ krb5_principal set_password_for,
+ int *result_code, krb5_data *result_code_string,
+ krb5_data *result_string)
+{
+ krb5_data chpw_rep;
+ krb5_address remote_kaddr;
+ krb5_boolean useTcp = 0;
+ GETSOCKNAME_ARG3_TYPE addrlen;
+ krb5_error_code code = 0;
+ char *code_string;
+ int local_result_code;
+
+ struct sendto_callback_context callback_ctx;
+ struct sendto_callback_info callback_info;
+ struct sockaddr_storage remote_addr;
+ struct addrlist al = ADDRLIST_INIT;
+
+ memset( &callback_ctx, 0, sizeof(struct sendto_callback_context));
+ callback_ctx.context = context;
+ callback_ctx.newpw = newpw;
+ callback_ctx.set_password_for = set_password_for;
+
+ if ((code = krb5_auth_con_init(callback_ctx.context,
+ &callback_ctx.auth_context)))
+ goto cleanup;
+
+ if ((code = krb5_mk_req_extended(callback_ctx.context,
+ &callback_ctx.auth_context,
+ AP_OPTS_USE_SUBKEY,
+ NULL,
+ creds,
+ &callback_ctx.ap_req)))
+ goto cleanup;
+
+ do {
+ if ((code = krb5_locate_kpasswd(callback_ctx.context,
+ krb5_princ_realm(callback_ctx.context,
+ creds->server),
+ &al, useTcp)))
+ break;
addrlen = sizeof(remote_addr);
- if (getpeername(s2, ss2sa(&remote_addr), &addrlen) < 0) {
- if (SOCKET_ERRNO == ECONNREFUSED || SOCKET_ERRNO == EHOSTUNREACH)
- continue; /* try the next addr */
- code = SOCKET_ERRNO;
- goto cleanup;
+ callback_info.context = (void*) &callback_ctx;
+ callback_info.pfn_callback = kpasswd_sendto_msg_callback;
+ callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
+
+ if ((code = krb5int_sendto(callback_ctx.context,
+ NULL,
+ &al,
+ &callback_info,
+ &chpw_rep,
+ NULL,
+ NULL,
+ ss2sa(&remote_addr),
+ &addrlen,
+ NULL
+ ))) {
+
+ /*
+ * Here we may want to switch to TCP on some errors.
+ * right?
+ */
+ break;
}
remote_kaddr.addrtype = ADDRTYPE_INET;
remote_kaddr.length = sizeof(ss2sin(&remote_addr)->sin_addr);
remote_kaddr.contents = (krb5_octet *) &ss2sin(&remote_addr)->sin_addr;
- /* mk_priv requires that the local address be set.
- getsockname is used for this. rd_priv requires that the
- remote address be set. recvfrom is used for this. If
- rd_priv is given a local address, and the message has the
- recipient addr in it, this will be checked. However, there
- is simply no way to know ahead of time what address the
- message will be delivered *to*. Therefore, it is important
- that either no recipient address is in the messages when
- mk_priv is called, or that no local address is passed to
- rd_priv. Both is a better idea, and I have done that. In
- summary, when mk_priv is called, *only* a local address is
- specified. when rd_priv is called, *only* a remote address
- is specified. Are we having fun yet? */
-
- if ((code = krb5_auth_con_setaddrs(context, auth_context,
- &local_kaddr, NULL))) {
- goto cleanup;
- }
-
- if( set_password_for )
- code = krb5int_mk_setpw_req(context, auth_context, &ap_req, set_password_for, newpw, &chpw_req);
+ if ((code = krb5_auth_con_setaddrs(callback_ctx.context,
+ callback_ctx.auth_context,
+ NULL,
+ &remote_kaddr)))
+ break;
+
+ if (set_password_for)
+ code = krb5int_rd_setpw_rep(callback_ctx.context,
+ callback_ctx.auth_context,
+ &chpw_rep,
+ &local_result_code,
+ result_string);
else
- code = krb5int_mk_chpw_req(context, auth_context, &ap_req, newpw, &chpw_req);
- if (code)
- {
- goto cleanup;
- }
-
- if ((cc = sendto(s1, chpw_req.data,
- (GETSOCKNAME_ARG3_TYPE) chpw_req.length, 0,
- al.addrs[i].ai->ai_addr, al.addrs[i].ai->ai_addrlen))
- != chpw_req.length)
- {
- if ((cc < 0) && ((SOCKET_ERRNO == ECONNREFUSED) ||
- (SOCKET_ERRNO == EHOSTUNREACH)))
- continue; /* try the next addr */
-
- code = (cc < 0) ? SOCKET_ERRNO : ECONNABORTED;
- goto cleanup;
- }
-
- chpw_rep.length = 1500;
- chpw_rep.data = (char *) malloc(chpw_rep.length);
-
- /* XXX need a timeout/retry loop here */
- FD_ZERO (&fdset);
- FD_SET (s1, &fdset);
- timeout.tv_sec = 120;
- timeout.tv_usec = 0;
- switch (select (s1 + 1, &fdset, 0, 0, &timeout)) {
- case -1:
- code = SOCKET_ERRNO;
- goto cleanup;
- case 0:
- code = ETIMEDOUT;
- goto cleanup;
- default:
- /* fall through */
- ;
- }
+ code = krb5int_rd_chpw_rep(callback_ctx.context,
+ callback_ctx.auth_context,
+ &chpw_rep,
+ &local_result_code,
+ result_string);
+
+ if (code) {
+ if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) {
+ krb5int_free_addrlist (&al);
+ useTcp = 1;
+ continue;
+ }
- /* "recv" would be good enough here... except that Windows/NT
- commits the atrocity of returning -1 to indicate failure,
- but leaving errno set to 0.
-
- "recvfrom(...,NULL,NULL)" would seem to be a good enough
- alternative, and it works on NT, but it doesn't work on
- SunOS 4.1.4 or Irix 5.3. Thus we must actually accept the
- value and discard it. */
- tmp_len = sizeof(tmp_addr);
- if ((cc = recvfrom(s1, chpw_rep.data,
- (GETSOCKNAME_ARG3_TYPE) chpw_rep.length,
- 0, ss2sa(&tmp_addr), &tmp_len)) < 0)
- {
- code = SOCKET_ERRNO;
- goto cleanup;
+ break;
}
- closesocket(s1);
- s1 = INVALID_SOCKET;
- closesocket(s2);
- s2 = INVALID_SOCKET;
-
- chpw_rep.length = cc;
-
- if ((code = krb5_auth_con_setaddrs(context, auth_context,
- NULL, &remote_kaddr)))
- goto cleanup;
-
- if( set_password_for )
- code = krb5int_rd_setpw_rep(context, auth_context, &chpw_rep, &local_result_code, result_string);
- else
- code = krb5int_rd_chpw_rep(context, auth_context, &chpw_rep, &local_result_code, result_string);
- if (code)
- goto cleanup;
-
if (result_code)
*result_code = local_result_code;
-
+
if (result_code_string) {
- if( set_password_for )
- code = krb5int_setpw_result_code_string(context, local_result_code, (const char **)&code_string);
- else
- code = krb5_chpw_result_code_string(context, local_result_code, &code_string);
- if(code)
- goto cleanup;
+ if (set_password_for)
+ code = krb5int_setpw_result_code_string(callback_ctx.context,
+ local_result_code,
+ (const char **)&code_string);
+ else
+ code = krb5_chpw_result_code_string(callback_ctx.context,
+ local_result_code,
+ &code_string);
+ if(code)
+ goto cleanup;
result_code_string->length = strlen(code_string);
result_code_string->data = malloc(result_code_string->length);
strncpy(result_code_string->data, code_string, result_code_string->length);
}
- code = 0;
- goto cleanup;
- }
-
- if (tried_one)
- /* Got some non-fatal errors, but didn't get any successes. */
- code = SOCKET_ERRNO;
- else
- /* Had some addresses, but didn't try any because they weren't
- AF_INET addresses and we don't support AF_INET6 addresses
- here yet. */
- code = EHOSTUNREACH;
+ if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) {
+ krb5int_free_addrlist (&al);
+ useTcp = 1;
+ } else {
+ break;
+ }
+ } while (TRUE);
cleanup:
- if (auth_context != NULL)
- krb5_auth_con_free(context, auth_context);
+ if (callback_ctx.auth_context != NULL)
+ krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context);
krb5int_free_addrlist (&al);
-
- if (s1 != INVALID_SOCKET)
- closesocket(s1);
-
- if (s2 != INVALID_SOCKET)
- closesocket(s2);
-
- krb5_free_data_contents(context, &chpw_req);
- krb5_free_data_contents(context, &chpw_rep);
- krb5_free_data_contents(context, &ap_req);
+ krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req);
return(code);
}
int *result_code, krb5_data *result_code_string, krb5_data *result_string
)
{
- krb5_creds creds;
- krb5_creds *credsp;
- krb5_error_code code;
+ krb5_creds creds;
+ krb5_creds *credsp;
+ krb5_error_code code;
-/*
-** get the proper creds for use with krb5_set_password -
-*/
- memset( &creds, 0, sizeof(creds) );
-/*
-** first get the principal for the password service -
-*/
- code = krb5_cc_get_principal( context, ccache, &creds.client );
- if( !code )
- {
- code = krb5_build_principal( context, &creds.server,
- krb5_princ_realm(context, change_password_for)->length,
- krb5_princ_realm(context, change_password_for)->data,
- "kadmin", "changepw", NULL );
- if(!code)
- {
- code = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
- if( ! code )
- {
- code = krb5_set_password(context, credsp, newpw, change_password_for,
- result_code, result_code_string,
- result_string);
- krb5_free_creds(context, credsp);
- }
- }
- krb5_free_cred_contents(context, &creds);
+ /*
+ ** get the proper creds for use with krb5_set_password -
+ */
+ memset (&creds, 0, sizeof(creds));
+ /*
+ ** first get the principal for the password service -
+ */
+ code = krb5_cc_get_principal (context, ccache, &creds.client);
+ if (!code) {
+ code = krb5_build_principal(context, &creds.server,
+ krb5_princ_realm(context, change_password_for)->length,
+ krb5_princ_realm(context, change_password_for)->data,
+ "kadmin", "changepw", NULL);
+ if (!code) {
+ code = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
+ if (!code) {
+ code = krb5_set_password(context, credsp, newpw, change_password_for,
+ result_code, result_code_string,
+ result_string);
+ krb5_free_creds(context, credsp);
+ }
}
- return code;
+ krb5_free_cred_contents(context, &creds);
+ }
+ return code;
}
}
if (addrs.naddrs > 0) {
- retval = krb5int_sendto (context, message, &addrs, reply, 0, 0,
- &addr_used);
+ retval = krb5int_sendto (context, message, &addrs, 0, reply, 0, 0,
+ 0, 0, &addr_used);
if (retval == 0) {
/*
* Set use_master to 1 if we ended up talking to a master when
#include "cm.h"
-static const char *const state_strings[] = {
- "INITIALIZING", "CONNECTING", "WRITING", "READING", "FAILED"
-};
-enum conn_states { INITIALIZING, CONNECTING, WRITING, READING, FAILED };
-struct incoming_krb5_message {
- size_t bufsizebytes_read;
- size_t bufsize;
- char *buf;
- char *pos;
- unsigned char bufsizebytes[4];
- size_t n_left;
-};
-struct conn_state {
- SOCKET fd;
- krb5_error_code err;
- enum conn_states state;
- unsigned int is_udp : 1;
- int (*service)(struct conn_state *, struct select_state *, int);
- struct addrinfo *addr;
- struct {
- struct {
- sg_buf sgbuf[2];
- sg_buf *sgp;
- int sg_count;
- } out;
- struct incoming_krb5_message in;
- } x;
-};
-
static int getcurtime (struct timeval *tvp)
{
#ifdef _WIN32
static int service_udp_fd (struct conn_state *conn,
struct select_state *selstate, int ssflags);
+static void
+set_conn_state_msg_length (struct conn_state *state, const krb5_data *message)
+{
+ if (!message || message->length == 0)
+ return;
+
+ if (!state->is_udp) {
+
+ state->x.out.msg_len_buf[0] = (message->length >> 24) & 0xff;
+ state->x.out.msg_len_buf[1] = (message->length >> 16) & 0xff;
+ state->x.out.msg_len_buf[2] = (message->length >> 8) & 0xff;
+ state->x.out.msg_len_buf[3] = message->length & 0xff;
+
+ SG_SET(&state->x.out.sgbuf[0], state->x.out.msg_len_buf, 4);
+ SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
+ state->x.out.sg_count = 2;
+
+ } else {
+
+ SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
+ SG_SET(&state->x.out.sgbuf[1], 0, 0);
+ state->x.out.sg_count = 1;
+
+ }
+}
+
+
static int
setup_connection (struct conn_state *state, struct addrinfo *ai,
- const krb5_data *message, unsigned char *message_len_buf,
- char **udpbufp)
+ const krb5_data *message, char **udpbufp)
{
state->state = INITIALIZING;
state->err = 0;
state->fd = INVALID_SOCKET;
SG_SET(&state->x.out.sgbuf[1], 0, 0);
if (ai->ai_socktype == SOCK_STREAM) {
+ /*
SG_SET(&state->x.out.sgbuf[0], message_len_buf, 4);
SG_SET(&state->x.out.sgbuf[1], message->data, message->length);
state->x.out.sg_count = 2;
+ */
+
state->is_udp = 0;
state->service = service_tcp_fd;
+ set_conn_state_msg_length (state, message);
} else {
+ /*
SG_SET(&state->x.out.sgbuf[0], message->data, message->length);
SG_SET(&state->x.out.sgbuf[1], 0, 0);
state->x.out.sg_count = 1;
+ */
+
state->is_udp = 1;
state->service = service_udp_fd;
+ set_conn_state_msg_length (state, message);
if (*udpbufp == 0) {
*udpbufp = malloc(krb5_max_dgram_size);
}
static int
-start_connection (struct conn_state *state, struct select_state *selstate)
+start_connection (struct conn_state *state,
+ struct select_state *selstate,
+ struct sendto_callback_info* callback_info,
+ krb5_data* callback_buffer)
{
int fd, e;
struct addrinfo *ai = state->addr;
*/
if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
state->state = CONNECTING;
+ state->fd = fd;
} else {
dprint("connect failed: %m\n", SOCKET_ERRNO);
(void) closesocket(fd);
* stack is broken, but if they gave us a connection, use it.
*/
state->state = WRITING;
+ state->fd = fd;
}
dprint("new state = %s\n", state_strings[state->state]);
- 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,
+ callback_info->context,
+ 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;
+ }
+
+ dprint("callback %p (message=%d@%p)\n",
+ state,
+ callback_buffer->length,
+ callback_buffer->data);
+
+ set_conn_state_msg_length( state, callback_buffer );
+ }
if (ai->ai_socktype == SOCK_DGRAM) {
/* Send it now. */
(void) closesocket(state->fd);
state->fd = INVALID_SOCKET;
state->state = FAILED;
- return -3;
+ return -4;
} else {
state->state = READING;
}
Otherwise, the caller should immediately move on to process the
next connection. */
static int
-maybe_send (struct conn_state *conn, struct select_state *selstate)
+maybe_send (struct conn_state *conn,
+ struct select_state *selstate,
+ struct sendto_callback_info* callback_info,
+ krb5_data* callback_buffer)
{
sg_buf *sg;
state_strings[conn->state],
conn->is_udp ? "udp" : "tcp");
if (conn->state == INITIALIZING)
- return start_connection(conn, selstate);
+ return start_connection(conn, selstate, callback_info, callback_buffer);
/* Did we already shut down this channel? */
if (conn->state == FAILED) {
krb5_error_code
krb5int_sendto (krb5_context context, const krb5_data *message,
- const struct addrlist *addrs, krb5_data *reply,
- struct sockaddr *localaddr, socklen_t *localaddrlen,
- int *addr_used)
+ const struct addrlist *addrs,
+ struct sendto_callback_info* callback_info, krb5_data *reply,
+ struct sockaddr *localaddr, socklen_t *localaddrlen,
+ struct sockaddr *remoteaddr, socklen_t *remoteaddrlen,
+ int *addr_used)
{
int i, pass;
int delay_this_pass = 2;
krb5_error_code retval;
struct conn_state *conns;
+ krb5_data *callback_data = 0;
size_t n_conns, host;
struct select_state *sel_state;
struct timeval now;
int winning_conn = -1, e = 0;
- unsigned char message_len_buf[4];
char *udpbuf = 0;
- dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data);
+ if (message)
+ dprint("krb5int_sendto(message=%d@%p, addrlist=", message->length, message->data);
+ else
+ dprint("krb5int_sendto(callback=%p, addrlist=", callback_info);
print_addrlist(addrs);
dprint(")\n");
if (conns == NULL) {
return ENOMEM;
}
+
memset(conns, 0, n_conns * sizeof(conns[i]));
+
+ if (callback_info) {
+ callback_data = malloc(n_conns * sizeof(krb5_data));
+ if (callback_data == NULL) {
+ return ENOMEM;
+ }
+
+ memset(conns, 0, n_conns * sizeof(callback_data[i]));
+ }
+
for (i = 0; i < n_conns; i++) {
conns[i].fd = INVALID_SOCKET;
}
FD_ZERO(&sel_state->wfds);
FD_ZERO(&sel_state->xfds);
- message_len_buf[0] = (message->length >> 24) & 0xff;
- message_len_buf[1] = (message->length >> 16) & 0xff;
- message_len_buf[2] = (message->length >> 8) & 0xff;
- message_len_buf[3] = message->length & 0xff;
/* Set up connections. */
for (host = 0; host < n_conns; host++) {
- retval = setup_connection (&conns[host], addrs->addrs[host].ai,
- message, message_len_buf, &udpbuf);
+ retval = setup_connection(&conns[host],
+ addrs->addrs[host].ai,
+ message,
+ &udpbuf);
if (retval)
continue;
}
dprint("host %d\n", host);
/* Send to the host, wait for a response, then move on. */
- if (maybe_send(&conns[host], sel_state))
+ if (maybe_send(&conns[host],
+ sel_state,
+ callback_info,
+ (callback_info ? &callback_data[host] : NULL)))
continue;
retval = getcurtime(&now);
*addr_used = winning_conn;
if (localaddr != 0 && localaddrlen != 0 && *localaddrlen > 0)
(void) getsockname(conns[winning_conn].fd, localaddr, localaddrlen);
+
+ if (remoteaddr != 0 && remoteaddrlen != 0 && *remoteaddrlen > 0)
+ (void) getpeername(conns[winning_conn].fd, remoteaddr, remoteaddrlen);
+
egress:
for (i = 0; i < n_conns; i++) {
if (conns[i].fd != INVALID_SOCKET)
&& conns[i].x.in.buf != 0
&& conns[i].x.in.buf != udpbuf)
free(conns[i].x.in.buf);
+ if (callback_info) {
+ callback_info->pfn_cleanup( callback_info->context, &callback_data[i]);
+ }
}
+
+ if (callback_data)
+ free(callback_data);
+
free(conns);
if (reply->data != udpbuf)
free(udpbuf);