From e7937d107abfdb747447bb642d7dbdf2aeab4fd3 Mon Sep 17 00:00:00 2001 From: Tom Yu Date: Sat, 6 Dec 1997 08:01:27 +0000 Subject: [PATCH] * Makefile.in: Add changepw.c, prompter.c * changepw.c: New file; Cygnus password changing protocol. * locate_kdc.c: Add parameter to indicate master KDC. * os-proto.h: Reflect changes to locate_kdc. * prompter.c: New file; Cygnus initial creds. * sendto_kdc.c: Add parameter to indicate master KDC. git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@10322 dc483132-0cff-0310-8789-dd5450dbe970 --- src/lib/krb5/os/ChangeLog | 14 ++ src/lib/krb5/os/Makefile.in | 6 + src/lib/krb5/os/changepw.c | 395 +++++++++++++++++++++++++++++++++++ src/lib/krb5/os/locate_kdc.c | 140 +++++++++---- src/lib/krb5/os/os-proto.h | 2 + src/lib/krb5/os/prompter.c | 128 ++++++++++++ src/lib/krb5/os/sendto_kdc.c | 24 ++- 7 files changed, 668 insertions(+), 41 deletions(-) create mode 100644 src/lib/krb5/os/changepw.c create mode 100644 src/lib/krb5/os/prompter.c diff --git a/src/lib/krb5/os/ChangeLog b/src/lib/krb5/os/ChangeLog index ac18a7345..39f0c6265 100644 --- a/src/lib/krb5/os/ChangeLog +++ b/src/lib/krb5/os/ChangeLog @@ -1,3 +1,17 @@ +Sat Dec 6 02:34:50 1997 Tom Yu + + * Makefile.in: Add changepw.c, prompter.c. + + * changepw.c: New file; Cygnus password changing protocol. + + * locate_kdc.c: Add parameter to indicate master KDC. + + * os-proto.h: Reflect changes to locate_kdc. + + * prompter.c: New file; Cygnus initial creds. + + * sendto_kdc.c: Add parameter to indicate master KDC. + Mon Oct 6 11:40:11 1997 Ezra Peisach * t_std_conf.c (main): Call krb5_free_context when done. diff --git a/src/lib/krb5/os/Makefile.in b/src/lib/krb5/os/Makefile.in index db3f3f1d9..aeea59e76 100644 --- a/src/lib/krb5/os/Makefile.in +++ b/src/lib/krb5/os/Makefile.in @@ -13,6 +13,7 @@ STLIBOBJS= \ def_realm.o \ DNR.o \ ccdefname.o \ + changepw.o \ free_krbhs.o \ free_hstrl.o \ full_ipadr.o \ @@ -36,6 +37,7 @@ STLIBOBJS= \ net_write.o \ osconfig.o \ port2ip.o \ + prompter.o \ promptusr.o \ read_msg.o \ read_pwd.o \ @@ -53,6 +55,7 @@ OBJS= \ def_realm.$(OBJEXT) \ DNR.$(OBJEXT) \ ccdefname.$(OBJEXT) \ + changepw.$(OBJEXT) \ free_krbhs.$(OBJEXT) \ free_hstrl.$(OBJEXT) \ full_ipadr.$(OBJEXT) \ @@ -76,6 +79,7 @@ OBJS= \ net_write.$(OBJEXT) \ osconfig.$(OBJEXT) \ port2ip.$(OBJEXT) \ + prompter.$(OBJEXT) \ promptusr.$(OBJEXT) \ read_msg.$(OBJEXT) \ read_pwd.$(OBJEXT) \ @@ -93,6 +97,7 @@ SRCS= \ $(srcdir)/def_realm.c \ $(srcdir)/DNR.c \ $(srcdir)/ccdefname.c \ + $(srcdir)/changepw.c \ $(srcdir)/free_krbhs.c \ $(srcdir)/free_hstrl.c \ $(srcdir)/full_ipadr.c \ @@ -115,6 +120,7 @@ SRCS= \ $(srcdir)/net_read.c \ $(srcdir)/net_write.c \ $(srcdir)/osconfig.c \ + $(srcdir)/prompter.c \ $(srcdir)/promptusr.c \ $(srcdir)/read_msg.c \ $(srcdir)/read_pwd.c \ diff --git a/src/lib/krb5/os/changepw.c b/src/lib/krb5/os/changepw.c new file mode 100644 index 000000000..78d9cd29c --- /dev/null +++ b/src/lib/krb5/os/changepw.c @@ -0,0 +1,395 @@ +/* + * lib/krb5/os/changepw.c + * + * Copyright 1990 by the Massachusetts Institute of Technology. + * All Rights Reserved. + * + * Export of this software from the United States of America may + * require a specific license from the United States Government. + * It is the responsibility of any person or organization contemplating + * export to obtain such a license before exporting. + * + * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and + * distribute this software and its documentation for any purpose and + * without fee is hereby granted, provided that the above copyright + * notice appear in all copies and that both that copyright notice and + * this permission notice appear in supporting documentation, and that + * the name of M.I.T. not be used in advertising or publicity pertaining + * to distribution of the software without specific, written prior + * permission. M.I.T. makes no representations about the suitability of + * this software for any purpose. It is provided "as is" without express + * or implied warranty. + * + */ + +#define NEED_SOCKETS +#include "k5-int.h" +#include "adm_err.h" + +#include +#include + +/* Win32 defines. */ +#if defined(_WIN32) && !defined(__CYGWIN32__) +#ifndef ECONNABORTED +#define ECONNABORTED WSAECONNABORTED +#endif +#ifndef ECONNREFUSED +#define ECONNREFUSED WSAECONNREFUSED +#endif +#ifndef EHOSTUNREACH +#define EHOSTUNREACH WSAEHOSTUNREACH +#endif +#endif /* _WIN32 && !__CYGWIN32__ */ + +KRB5_DLLIMP krb5_error_code KRB5_CALLCONV +krb5_change_password(context, creds, newpw, result_code, + result_code_string, result_string) + krb5_context context; + krb5_creds *creds; + char *newpw; + int *result_code; + krb5_data *result_code_string; + krb5_data *result_string; +{ + krb5_auth_context auth_context; + krb5_data ap_req, chpw_req, chpw_rep; + krb5_address local_kaddr, remote_kaddr; + const char *realm_kdc_names[4]; + int default_port; + char **hostlist, *host, *port, *cp, *code_string; + krb5_error_code code; + int i, j, out, count, addrlen; + struct sockaddr *addr_p, local_addr, remote_addr, tmp_addr; + struct sockaddr_in *sin_p; + struct hostent *hp; + struct servent *sp; +#ifdef KRB5_USE_INET + u_short udpport = htons(KRB5_DEFAULT_PORT); +#endif + int cc, local_result_code, tmp_len; + SOCKET s1, s2; + + auth_context = NULL; + + if (code = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY, + NULL, creds, &ap_req)) + return(code); + + if ((host = malloc(krb5_princ_realm(context, creds->client)->length + 1)) + == NULL) + return ENOMEM; + + strncpy(host, krb5_princ_realm(context, creds->client)->data, + krb5_princ_realm(context, creds->client)->length); + host[krb5_princ_realm(context, creds->client)->length] = '\0'; + hostlist = 0; + + realm_kdc_names[0] = "realms"; + realm_kdc_names[1] = host; + realm_kdc_names[2] = "kpasswd_server"; + realm_kdc_names[3] = 0; + + default_port = 0; + + code = profile_get_values(context->profile, realm_kdc_names, &hostlist); + + if (code == PROF_NO_RELATION) { + realm_kdc_names[2] = "admin_server"; + + default_port = 1; + + code = profile_get_values(context->profile, realm_kdc_names, + &hostlist); + } + + krb5_xfree(host); + + if (code == PROF_NO_SECTION) + return KRB5_REALM_UNKNOWN; + else if (code == PROF_NO_RELATION) + return KRB5_CONFIG_BADFORMAT; + else if (code) + return code; + +#ifdef KRB5_USE_INET + /* XXX should look for "kpasswd" in /etc/services */ + udpport = htons(DEFAULT_KPASSWD_PORT); +#endif + + count = 0; + while (hostlist && hostlist[count]) + count++; + + if (count == 0) + /* XXX */ + return(KADM_NO_HOST); + + addr_p = (struct sockaddr *) malloc(sizeof(struct sockaddr) * count); + + host = hostlist[0]; + out = 0; + + /* + * Strip off excess whitespace + */ + cp = strchr(host, ' '); + if (cp) + *cp = 0; + cp = strchr(host, '\t'); + if (cp) + *cp = 0; + port = strchr(host, ':'); + if (port) { + *port = 0; + port++; + /* if the admin_server line was used, ignore the specified + port */ + if (default_port) + port = NULL; + } + hp = gethostbyname(hostlist[0]); + + if (hp != 0) { + switch (hp->h_addrtype) { +#ifdef KRB5_USE_INET + case AF_INET: + for (j=0; hp->h_addr_list[j]; j++) { + sin_p = (struct sockaddr_in *) &addr_p[out++]; + memset ((char *)sin_p, 0, sizeof(struct sockaddr)); + sin_p->sin_family = hp->h_addrtype; + sin_p->sin_port = port ? htons(atoi(port)) : udpport; + memcpy((char *)&sin_p->sin_addr, + (char *)hp->h_addr_list[j], + sizeof(struct in_addr)); + if (out+1 >= count) { + count += 5; + addr_p = (struct sockaddr *) + realloc ((char *)addr_p, + sizeof(struct sockaddr) * count); + } + } + break; +#endif + default: + break; + } + } + + for (i=0; hostlist[i]; i++) + free(hostlist[i]); + free ((char *)hostlist); + + if (out == 0) { /* Couldn't resolve any KDC names */ + free (addr_p); + return(KADM_NO_HOST); + } + + /* 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) { + free(addr_p); + return(errno); + } + + if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) { + free(addr_p); + return(errno); + } + + for (i=0; isin_addr.s_addr != 0) { + local_kaddr.addrtype = ADDRTYPE_INET; + local_kaddr.length = + sizeof(((struct sockaddr_in *) &local_addr)->sin_addr); + local_kaddr.contents = + (char *) &(((struct sockaddr_in *) &local_addr)->sin_addr); + } else { + krb5_address **addrs; + + krb5_os_localaddr(context, &addrs); + 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); + + krb5_free_addresses(context, addrs); + } + + addrlen = sizeof(remote_addr); + if (getpeername(s2, &remote_addr, &addrlen) < 0) { +#ifndef HAVE_MACSOCK_H + if ((errno == ECONNREFUSED) || + (errno == EHOSTUNREACH)) +#endif + continue; /* try the next addr */ + free(addr_p); + closesocket(s1); + closesocket(s2); + return(errno); + } + + remote_kaddr.addrtype = ADDRTYPE_INET; + remote_kaddr.length = + sizeof(((struct sockaddr_in *) &remote_addr)->sin_addr); + remote_kaddr.contents = + (char *) &(((struct sockaddr_in *) &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)) { + free(addr_p); + closesocket(s1); + closesocket(s2); + return(code); + } + + if (code = krb5_mk_chpw_req(context, auth_context, &ap_req, + newpw, &chpw_req)) { + free(addr_p); + closesocket(s1); + closesocket(s2); + return(code); + } + + if ((cc = sendto(s1, chpw_req.data, chpw_req.length, 0, + (struct sockaddr *) &addr_p[i], + sizeof(addr_p[i]))) != + chpw_req.length) { +#ifndef HAVE_MACSOCK_H + if ((cc < 0) && ((errno == ECONNREFUSED) || + (errno == EHOSTUNREACH))) +#endif + continue; /* try the next addr */ + free(addr_p); + closesocket(s1); + closesocket(s2); + return((cc < 0)?errno:ECONNABORTED); + } + + krb5_xfree(chpw_req.data); + + chpw_rep.length = 1500; + chpw_rep.data = (char *) malloc(chpw_rep.length); + + /* XXX need a timeout/retry loop here */ + + /* "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, chpw_rep.length, 0, &tmp_addr, &tmp_len)) < 0) { + free(addr_p); + closesocket(s1); + closesocket(s2); + return(errno); + } + + closesocket(s1); + closesocket(s2); + + chpw_rep.length = cc; + + if (code = krb5_auth_con_setaddrs(context, auth_context, NULL, + &remote_kaddr)) { + free(addr_p); + closesocket(s1); + closesocket(s2); + return(code); + } + + code = krb5_rd_chpw_rep(context, auth_context, &chpw_rep, + &local_result_code, result_string); + + free(chpw_rep.data); + free(addr_p); + + if (code) + return(code); + + if (result_code) + *result_code = local_result_code; + + if (result_code_string) { + if (code = krb5_chpw_result_code_string(context, local_result_code, + &code_string)) + return(code); + + result_code_string->length = strlen(code_string); + if ((result_code_string->data = + (char *) malloc(result_code_string->length)) == NULL) + return(ENOMEM); + strncpy(result_code_string->data, code_string, + result_code_string->length); + } + + return(0); + } + + free(addr_p); + closesocket(s1); + closesocket(s2); + return(errno); +} diff --git a/src/lib/krb5/os/locate_kdc.c b/src/lib/krb5/os/locate_kdc.c index 3f31216dd..7530e0966 100644 --- a/src/lib/krb5/os/locate_kdc.c +++ b/src/lib/krb5/os/locate_kdc.c @@ -30,17 +30,21 @@ /* * returns count of number of addresses found + * if master is non-NULL, it is filled in with the index of + * the master kdc */ krb5_error_code -krb5_locate_kdc(context, realm, addr_pp, naddrs) +krb5_locate_kdc(context, realm, addr_pp, naddrs, master_index, nmasters) krb5_context context; const krb5_data *realm; struct sockaddr **addr_pp; int *naddrs; + int *master_index; + int *nmasters; { const char *realm_kdc_names[4]; - char **hostlist, *host, *port, *cp; + char **masterlist, **hostlist, *host, *port, *cp; krb5_error_code code; int i, j, out, count; struct sockaddr *addr_p; @@ -58,7 +62,9 @@ krb5_locate_kdc(context, realm, addr_pp, naddrs) strncpy(host, realm->data, realm->length); host[realm->length] = '\0'; hostlist = 0; - + + masterlist = NULL; + realm_kdc_names[0] = "realms"; realm_kdc_names[1] = host; realm_kdc_names[2] = "kdc"; @@ -67,12 +73,16 @@ krb5_locate_kdc(context, realm, addr_pp, naddrs) code = profile_get_values(context->profile, realm_kdc_names, &hostlist); krb5_xfree(host); - if (code == PROF_NO_SECTION) - return KRB5_REALM_UNKNOWN; - if (code == PROF_NO_RELATION) - return KRB5_CONFIG_BADFORMAT; - if (code) - return code; + if (code == PROF_NO_SECTION) { + krb5_xfree(host); + return KRB5_REALM_UNKNOWN; + } else if (code == PROF_NO_RELATION) { + krb5_xfree(host); + return KRB5_CONFIG_BADFORMAT; + } else if (code) { + krb5_xfree(host); + return code; + } #ifdef HAVE_NETINET_IN_H if ((sp = getservbyname(KDC_PORTNAME, "udp"))) @@ -88,10 +98,52 @@ krb5_locate_kdc(context, realm, addr_pp, naddrs) count++; if (count == 0) { + krb5_xfree(host); *naddrs = 0; return 0; } + if (master_index) { + realm_kdc_names[0] = "realms"; + realm_kdc_names[1] = host; + realm_kdc_names[2] = "admin_server"; + realm_kdc_names[3] = 0; + + code = profile_get_values(context->profile, realm_kdc_names, + &masterlist); + + krb5_xfree(host); + + if (code) { + *master_index = 0; + *nmasters = 0; + } else { + for (i=0; masterlist[i]; i++) { + host = masterlist[i]; + + /* + * Strip off excess whitespace + */ + cp = strchr(host, ' '); + if (cp) + *cp = 0; + cp = strchr(host, '\t'); + if (cp) + *cp = 0; + cp = strchr(host, ':'); + if (cp) + *cp = 0; + } + } + } else { + krb5_xfree(host); + } + + /* at this point, is master is non-NULL, then either the master kdc + is required, and there is one, or the master kdc is not required, + and there may or may not be one. */ + + #ifdef HAVE_NETINET_IN_H if (sec_udpport) count = count * 2; @@ -115,40 +167,52 @@ krb5_locate_kdc(context, realm, addr_pp, naddrs) *port = 0; port++; } - hp = gethostbyname(hostlist[i]); - if (hp != 0) { - switch (hp->h_addrtype) { + + if ((hp = gethostbyname(hostlist[i])) == 0) { + free(hostlist[i]); + hostlist[i] = 0; + continue; + } + + if (masterlist) + for (j=0; masterlist[j]; j++) + if (strcasecmp(hostlist[i], masterlist[j]) == 0) + *master_index = out; + + switch (hp->h_addrtype) { + #ifdef HAVE_NETINET_IN_H - case AF_INET: - for (j=0; hp->h_addr_list[j]; j++) { - sin_p = (struct sockaddr_in *) &addr_p[out++]; - memset ((char *)sin_p, 0, sizeof(struct sockaddr)); - sin_p->sin_family = hp->h_addrtype; - sin_p->sin_port = port ? htons(atoi(port)) : udpport; - memcpy((char *)&sin_p->sin_addr, - (char *)hp->h_addr_list[j], - sizeof(struct in_addr)); - if (out+1 >= count) { - count += 5; - addr_p = (struct sockaddr *) - realloc ((char *)addr_p, - sizeof(struct sockaddr) * count); - } - if (sec_udpport && !port) { - addr_p[out] = addr_p[out-1]; - sin_p = (struct sockaddr_in *) &addr_p[out++]; - sin_p->sin_port = sec_udpport; - } + case AF_INET: + for (j=0; hp->h_addr_list[j]; j++) { + sin_p = (struct sockaddr_in *) &addr_p[out++]; + memset ((char *)sin_p, 0, sizeof(struct sockaddr)); + sin_p->sin_family = hp->h_addrtype; + sin_p->sin_port = port ? htons(atoi(port)) : udpport; + memcpy((char *)&sin_p->sin_addr, + (char *)hp->h_addr_list[j], + sizeof(struct in_addr)); + if (out+1 >= count) { + count += 5; + addr_p = (struct sockaddr *) + realloc ((char *)addr_p, + sizeof(struct sockaddr) * count); + } + if (sec_udpport && !port) { + addr_p[out] = addr_p[out-1]; + sin_p = (struct sockaddr_in *) &addr_p[out++]; + sin_p->sin_port = sec_udpport; } - break; -#endif - default: - break; } + break; +#endif + default: + break; } - free(hostlist[i]); - hostlist[i] = 0; + if (masterlist) + *nmasters = out - *master_index; } + + free ((char *)hostlist); if (out == 0) { /* Couldn't resolve any KDC names */ diff --git a/src/lib/krb5/os/os-proto.h b/src/lib/krb5/os/os-proto.h index 918a14f5e..02c186f88 100644 --- a/src/lib/krb5/os/os-proto.h +++ b/src/lib/krb5/os/os-proto.h @@ -32,6 +32,8 @@ krb5_error_code krb5_locate_kdc PROTOTYPE((krb5_context, const krb5_data *, struct sockaddr **, + int *, + int *, int *)); #endif diff --git a/src/lib/krb5/os/prompter.c b/src/lib/krb5/os/prompter.c new file mode 100644 index 000000000..10f9d541b --- /dev/null +++ b/src/lib/krb5/os/prompter.c @@ -0,0 +1,128 @@ +#include "k5-int.h" +#if !defined(_MSDOS) && (!defined(_WIN32) || (defined(_WIN32) && defined(__CYGWIN32__))) && !defined(_MACINTOSH) +#include +#include +#include +#include +#ifdef __vxworks +#define ECHO_PASSWORD +#endif + +#ifndef ECHO_PASSWORD +#include +#endif /* ECHO_PASSWORD */ + +static jmp_buf pwd_jump; + +static krb5_sigtype +intr_routine(signo) + int signo; +{ + longjmp(pwd_jump, 1); + /*NOTREACHED*/ +} + +KRB5_DLLIMP krb5_error_code KRB5_CALLCONV +krb5_prompter_posix(krb5_context context, + void *data, + const char *banner, + int num_prompts, + krb5_prompt prompts[]) +{ + /* adapted from Kerberos v5 krb5_read_password() */ + + register char *ptr; + int scratchchar; + krb5_sigtype (*ointrfunc)(); + krb5_error_code errcode; + int i; +#ifndef ECHO_PASSWORD + struct termios echo_control, save_control; + int fd; +#endif + + if (banner) { + fputs(banner, stdout); + fputs("\n", stdout); + } + + if (setjmp(pwd_jump)) { + errcode = KRB5_LIBOS_PWDINTR; /* we were interrupted... */ + goto cleanup; + } + /* save intrfunc */ + ointrfunc = signal(SIGINT, intr_routine); + + for (i=0; idata, 0, prompts[i].reply->length); + + if (fgets(prompts[i].reply->data, prompts[i].reply->length, stdin) + == NULL) { + if (prompts[i].hidden) + (void) putchar('\n'); + errcode = KRB5_LIBOS_CANTREADPWD; + goto cleanup; + } + if (prompts[i].hidden) + (void) putchar('\n'); + /* fgets always null-terminates the returned string */ + + /* replace newline with null */ + if ((ptr = strchr(prompts[i].reply->data, '\n'))) + *ptr = '\0'; + else /* flush rest of input line */ + do { + scratchchar = getchar(); + } while (scratchchar != EOF && scratchchar != '\n'); + + prompts[i].reply->length = strlen(prompts[i].reply->data); + +#ifndef ECHO_PASSWORD + if (prompts[i].hidden && (isatty(fd) == 1)) + if ((tcsetattr(fd, TCSANOW, &save_control) == -1) && + (errcode == 0)) + return errno; +#endif + } + + errcode = 0; + +cleanup: + (void) signal(SIGINT, ointrfunc); + return(errcode); +} +#else /* MSDOS */ + +KRB5_DLLIMP krb5_error_code KRB5_CALLCONV +krb5_prompter_posix(krb5_context context, + void *data, + const char *banner, + int num_prompts, + krb5_prompt prompts[]) +{ + return(EINVAL); +} +#endif /* !MSDOS */ + diff --git a/src/lib/krb5/os/sendto_kdc.c b/src/lib/krb5/os/sendto_kdc.c index 881066d08..ff14863b4 100644 --- a/src/lib/krb5/os/sendto_kdc.c +++ b/src/lib/krb5/os/sendto_kdc.c @@ -57,15 +57,16 @@ extern int krb5_skdc_timeout_shift; extern int krb5_skdc_timeout_1; krb5_error_code -krb5_sendto_kdc (context, message, realm, reply) +krb5_sendto_kdc (context, message, realm, reply, master) krb5_context context; const krb5_data * message; const krb5_data * realm; krb5_data * reply; + int *master; { register int timeout, host, i; struct sockaddr *addr; - int naddr; + int naddr, master_index, nmasters; int sent, nready; krb5_error_code retval; SOCKET *socklist; @@ -77,10 +78,14 @@ krb5_sendto_kdc (context, message, realm, reply) * find KDC location(s) for realm */ - if (retval = krb5_locate_kdc (context, realm, &addr, &naddr)) + if (retval = krb5_locate_kdc (context, realm, &addr, &naddr, + master?&master_index:NULL, + master?&nmasters:NULL)) return retval; if (naddr == 0) return KRB5_REALM_UNKNOWN; + if (master && (*master == 1) && (nmasters == 0)) + return KRB5_KDC_UNREACH; socklist = (SOCKET *)malloc(naddr * sizeof(SOCKET)); if (socklist == NULL) { @@ -120,6 +125,12 @@ krb5_sendto_kdc (context, message, realm, reply) timeout <<= krb5_skdc_timeout_shift) { sent = 0; for (host = 0; host < naddr; host++) { + /* if a master kdc is required, skip the non-master kdc's */ + + if (master && (*master == 1) && + ((host < master_index) || (host >= (master_index+nmasters)))) + continue; + /* send to the host, wait timeout seconds for a response, then move on. */ /* cache some sockets for each host */ @@ -196,6 +207,13 @@ krb5_sendto_kdc (context, message, realm, reply) reply->length = cc; retval = 0; + + /* if the caller asked to be informed if it + got a master kdc, tell it */ + if (master) + *master = ((host >= master_index) && + (host < (master_index+nmasters))); + goto out; } else if (nready == 0) { /* timeout */ -- 2.26.2