aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKen Raeburn <raeburn@mit.edu>2006-08-23 22:56:29 +0000
committerKen Raeburn <raeburn@mit.edu>2006-08-23 22:56:29 +0000
commit8d8fc0bdc087ec77418f0832969e1dfc6dfa1a7c (patch)
tree77a6d0835ce6a9876069ba288fb6b075b46c9519
parentf3abd2b4e19b2c9a2b81841c30ad93ac68fcb6d3 (diff)
downloadkrb5-8d8fc0bdc087ec77418f0832969e1dfc6dfa1a7c.zip
krb5-8d8fc0bdc087ec77418f0832969e1dfc6dfa1a7c.tar.gz
krb5-8d8fc0bdc087ec77418f0832969e1dfc6dfa1a7c.tar.bz2
Merge Todd's TCP changepw support, with a few fixups
* include/cm.h (state_strings, enum conn_states, struct incoming_krb5_message, struct conn_state): Moved here from lib/krb5/os/sendto_kdc.c. (stuct sendto_callback_info): New type. * lib/krb5/os/sendto_kdc.c (set_conn_state_msg_length): New function. (setup_connection): Deleted argument message_len_buf. Don't store message length; call set_conn_state_msg_length instead. (start_connection): New arguments callback_info and callback_buffer. Invoke callback function if any, and set message length on success. (maybe_send): New arguments callback_info and callback_buffer; pass them to start_connection. (krb5int_sendto): New arguments callback_info, remoteaddr, remoteaddrlen. If callback info is provided, allocate per-connection buffers, and pass them to maybe_send. On cleanup, invoke the cleanup callback function if any. (krb5_sendto_kdc): Update krb5int_sendto call. * include/k5-int.h (struct sendto_callback_info): Add forward declaration. (krb5int_sendto, struct _krb5int_access.sendto_udp): Update for new signature. * lib/krb5/os/send524 (krb5int_524_sendto_kdc): Update krb5int_sendto call. * lib/krb4/send_to_kdc.c (krb5int_send_to_kdc_addr): Update sendto_udp call. * lib/krb5/os/changepw.c (struct sendto_callback_context): New type. (krb5_locate_kpasswd): New argument useTcp, used to select socket type in krb5int_locate_server call. (kpasswd_sendto_msg_cleanup, kpasswd_sendto_msg_callback): New functions. (krb5_change_set_password): Call krb5int_sendto with callbacks, instead of managing the exchange here. On RESPONSE_TOO_BIG error, try again with TCP only. * lib/krb5/krb/chpw.c (krb5int_rd_chpw_rep): If length is wrong, check if a buggy server sent a KRB_ERROR. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@18518 dc483132-0cff-0310-8789-dd5450dbe970
-rw-r--r--src/include/cm.h42
-rw-r--r--src/include/k5-int.h17
-rw-r--r--src/lib/krb4/send_to_kdc.c4
-rw-r--r--src/lib/krb5/krb/chpw.c41
-rw-r--r--src/lib/krb5/os/changepw.c508
-rw-r--r--src/lib/krb5/os/send524.c2
-rw-r--r--src/lib/krb5/os/sendto_kdc.c166
7 files changed, 444 insertions, 336 deletions
diff --git a/src/include/cm.h b/src/include/cm.h
index 428e61e..716e6cb 100644
--- a/src/include/cm.h
+++ b/src/include/cm.h
@@ -32,10 +32,52 @@ struct select_state {
struct timeval end_time; /* magic: tv_sec==0 => never time out */
};
+
/* Select state flags. */
#define SSF_READ 0x01
#define SSF_WRITE 0x02
#define SSF_EXCEPTION 0x04
+
+static const char *const state_strings[] = {
+ "INITIALIZING", "CONNECTING", "WRITING", "READING", "FAILED"
+};
+
+
+/* connection states */
+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;
+ unsigned char msg_len_buf[4];
+ } out;
+ struct incoming_krb5_message in;
+ } x;
+};
+
+struct sendto_callback_info {
+ int (*pfn_callback) (struct conn_state *, void *, krb5_data *);
+ void (*pfn_cleanup) (void *, krb5_data *);
+ void *context;
+};
+
+
krb5_error_code krb5int_cm_call_select (const struct select_state *,
struct select_state *, int *);
diff --git a/src/include/k5-int.h b/src/include/k5-int.h
index a8b9e5d..a55fea7 100644
--- a/src/include/k5-int.h
+++ b/src/include/k5-int.h
@@ -487,15 +487,19 @@ extern char *strdup (const char *);
#include <stdio.h>
struct addrlist;
+struct sendto_callback_info;
/* libos.spec */
krb5_error_code krb5_lock_file (krb5_context, int, int);
krb5_error_code krb5_unlock_file (krb5_context, int);
krb5_error_code krb5_sendto_kdc (krb5_context, const krb5_data *,
const krb5_data *, krb5_data *, int *, int);
-krb5_error_code krb5int_sendto (krb5_context, const krb5_data *,
- const struct addrlist *, krb5_data *,
- struct sockaddr *, socklen_t *, int *);
+
+krb5_error_code krb5int_sendto (krb5_context context, const krb5_data *message,
+ 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);
+
krb5_error_code krb5_get_krbhst (krb5_context, const krb5_data *, char *** );
krb5_error_code krb5_free_krbhst (krb5_context, char * const * );
krb5_error_code krb5_create_secure_file (krb5_context, const char * pathname);
@@ -1610,7 +1614,7 @@ krb5int_generate_and_save_subkey (krb5_context, krb5_auth_context,
/* set and change password helpers */
krb5_error_code krb5int_mk_chpw_req
- (krb5_context context, krb5_auth_context auth_context,
+ (krb5_context context, krb5_auth_context auth_context,
krb5_data *ap_req, char *passwd, krb5_data *packet);
krb5_error_code krb5int_rd_chpw_rep
(krb5_context context, krb5_auth_context auth_context,
@@ -1673,8 +1677,9 @@ typedef struct _krb5int_access {
krb5_data *output);
/* service location and communication */
krb5_error_code (*sendto_udp) (krb5_context, const krb5_data *msg,
- const struct addrlist *, krb5_data *reply,
- struct sockaddr *, socklen_t *, int *);
+ const struct addrlist *, struct sendto_callback_info*, krb5_data *reply,
+ struct sockaddr *, socklen_t *,struct sockaddr *,
+ socklen_t *, int *);
krb5_error_code (*add_host_to_list)(struct addrlist *lp,
const char *hostname,
int port, int secport,
diff --git a/src/lib/krb4/send_to_kdc.c b/src/lib/krb4/send_to_kdc.c
index 3be677b..a33ad2b 100644
--- a/src/lib/krb4/send_to_kdc.c
+++ b/src/lib/krb4/send_to_kdc.c
@@ -180,8 +180,8 @@ krb4int_send_to_kdc_addr(
message.length = pkt->length;
message.data = (char *)pkt->dat; /* XXX yuck */
- retval = internals.sendto_udp(NULL, &message, &al, &reply, addr,
- addrlen, NULL);
+ retval = internals.sendto_udp(NULL, &message, &al, NULL, &reply, addr,
+ addrlen, NULL, 0, NULL);
DEB(("sendto_udp returns %d\n", retval));
free_al:
internals.free_addrlist(&al);
diff --git a/src/lib/krb5/krb/chpw.c b/src/lib/krb5/krb/chpw.c
index 6401246..427ea39 100644
--- a/src/lib/krb5/krb/chpw.c
+++ b/src/lib/krb5/krb/chpw.c
@@ -9,7 +9,12 @@
krb5_error_code
-krb5int_mk_chpw_req(krb5_context context, krb5_auth_context auth_context, krb5_data *ap_req, char *passwd, krb5_data *packet)
+krb5int_mk_chpw_req(
+ krb5_context context,
+ krb5_auth_context auth_context,
+ krb5_data *ap_req,
+ char *passwd,
+ krb5_data *packet)
{
krb5_error_code ret = 0;
krb5_data clearpw;
@@ -33,15 +38,15 @@ krb5int_mk_chpw_req(krb5_context context, krb5_auth_context auth_context, krb5_d
packet->length = 6 + ap_req->length + cipherpw.length;
packet->data = (char *) malloc(packet->length);
if (packet->data == NULL)
- {
+ {
ret = ENOMEM;
goto cleanup;
- }
+ }
ptr = packet->data;
/* length */
- *ptr++ = (packet->length>>8) & 0xff;
+ *ptr++ = (packet->length>> 8) & 0xff;
*ptr++ = packet->length & 0xff;
/* version == 0x0001 big-endian */
@@ -96,8 +101,30 @@ krb5int_rd_chpw_rep(krb5_context context, krb5_auth_context auth_context, krb5_d
plen = (*ptr++ & 0xff);
plen = (plen<<8) | (*ptr++ & 0xff);
- if (plen != packet->length)
- return(KRB5KRB_AP_ERR_MODIFIED);
+ if (plen != packet->length)
+ {
+ /*
+ * MS KDCs *may* send back a KRB_ERROR. Although
+ * not 100% correct via RFC3244, it's something
+ * we can workaround here.
+ */
+ if (krb5_is_krb_error(packet)) {
+
+ if ((ret = krb5_rd_error(context, packet, &krberror)))
+ return(ret);
+
+ if (krberror->e_data.data == NULL) {
+ ret = ERROR_TABLE_BASE_krb5 + (krb5_error_code) krberror->error;
+ krb5_free_error(context, krberror);
+ return (ret);
+ }
+ }
+ else
+ {
+ return(KRB5KRB_AP_ERR_MODIFIED);
+ }
+ }
+
/* verify version number */
@@ -367,7 +394,7 @@ krb5int_rd_setpw_rep( krb5_context context, krb5_auth_context auth_context, krb5
/*
** set password version is 0xff80, change password version is 1
*/
- if (version_number != 0xff80 && version_number != 1)
+ if (version_number != 1 && version_number != 0xff80)
return(KRB5KDC_ERR_BAD_PVNO);
/*
** now fill in ap_rep with the reply -
diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c
index 5f900b6..60f2d1d 100644
--- a/src/lib/krb5/os/changepw.c
+++ b/src/lib/krb5/os/changepw.c
@@ -27,11 +27,13 @@
/*
* 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>
@@ -40,26 +42,37 @@
#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);
@@ -70,253 +83,225 @@ krb5_locate_kpasswd(krb5_context context, const krb5_data *realm,
}
-/*
-** 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);
@@ -327,34 +312,20 @@ krb5_change_set_password(
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);
}
@@ -393,36 +364,33 @@ krb5_set_password_using_ccache(
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;
}
diff --git a/src/lib/krb5/os/send524.c b/src/lib/krb5/os/send524.c
index 09c9c90..f6e7a0b 100644
--- a/src/lib/krb5/os/send524.c
+++ b/src/lib/krb5/os/send524.c
@@ -98,7 +98,7 @@ krb5int_524_sendto_kdc (context, message, realm, reply, addr, addrlen)
if (al.naddrs == 0)
return KRB5_REALM_UNKNOWN;
- retval = krb5int_sendto (context, message, &al, reply, addr, addrlen, NULL);
+ retval = krb5int_sendto (context, message, &al, NULL, reply, addr, addrlen, NULL, 0, NULL);
krb5int_free_addrlist (&al);
return retval;
#else
diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c
index 75cb036..b616578 100644
--- a/src/lib/krb5/os/sendto_kdc.c
+++ b/src/lib/krb5/os/sendto_kdc.c
@@ -381,8 +381,8 @@ krb5_sendto_kdc (krb5_context context, const krb5_data *message,
}
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
@@ -448,35 +448,6 @@ krb5_sendto_kdc (krb5_context context, const krb5_data *message,
#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
@@ -552,11 +523,37 @@ static int service_tcp_fd (struct conn_state *conn,
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;
@@ -565,17 +562,25 @@ setup_connection (struct conn_state *state, struct addrinfo *ai,
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);
@@ -594,7 +599,10 @@ setup_connection (struct conn_state *state, struct addrinfo *ai,
}
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;
@@ -628,6 +636,7 @@ start_connection (struct conn_state *state, struct select_state *selstate)
*/
if (SOCKET_ERRNO == EINPROGRESS || SOCKET_ERRNO == EWOULDBLOCK) {
state->state = CONNECTING;
+ state->fd = fd;
} else {
dprint("connect failed: %m\n", SOCKET_ERRNO);
(void) closesocket(fd);
@@ -643,10 +652,36 @@ start_connection (struct conn_state *state, struct select_state *selstate)
* 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. */
@@ -660,7 +695,7 @@ start_connection (struct conn_state *state, struct select_state *selstate)
(void) closesocket(state->fd);
state->fd = INVALID_SOCKET;
state->state = FAILED;
- return -3;
+ return -4;
} else {
state->state = READING;
}
@@ -699,7 +734,10 @@ start_connection (struct conn_state *state, struct select_state *selstate)
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;
@@ -707,7 +745,7 @@ maybe_send (struct conn_state *conn, struct select_state *selstate)
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) {
@@ -1056,22 +1094,27 @@ service_fds (struct select_state *selstate,
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");
@@ -1083,7 +1126,18 @@ krb5int_sendto (krb5_context context, const krb5_data *message,
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;
}
@@ -1102,15 +1156,13 @@ krb5int_sendto (krb5_context context, const krb5_data *message,
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;
}
@@ -1122,7 +1174,10 @@ krb5int_sendto (krb5_context context, const krb5_data *message,
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);
@@ -1180,6 +1235,10 @@ krb5int_sendto (krb5_context context, const krb5_data *message,
*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)
@@ -1188,7 +1247,14 @@ egress:
&& 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);