* Makefile.in: Add changepw.c, prompter.c
authorTom Yu <tlyu@mit.edu>
Sat, 6 Dec 1997 08:01:27 +0000 (08:01 +0000)
committerTom Yu <tlyu@mit.edu>
Sat, 6 Dec 1997 08:01:27 +0000 (08:01 +0000)
* 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
src/lib/krb5/os/Makefile.in
src/lib/krb5/os/changepw.c [new file with mode: 0644]
src/lib/krb5/os/locate_kdc.c
src/lib/krb5/os/os-proto.h
src/lib/krb5/os/prompter.c [new file with mode: 0644]
src/lib/krb5/os/sendto_kdc.c

index ac18a73451ba876246544161f6adef0a0de347ac..39f0c6265091d0b7d051a5d21d664963ae67d4c4 100644 (file)
@@ -1,3 +1,17 @@
+Sat Dec  6 02:34:50 1997  Tom Yu  <tlyu@mit.edu>
+
+       * 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  <epeisach@kangaroo.mit.edu>
 
        * t_std_conf.c (main): Call krb5_free_context when done.
index db3f3f1d9b953b243c4b168559eadfec54202273..aeea59e761c6f612834cfbe2322bafaf3a6f8a0c 100644 (file)
@@ -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 (file)
index 0000000..78d9cd2
--- /dev/null
@@ -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 <stdio.h>
+#include <errno.h>
+
+/* 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; i<out; i++) {
+       if (connect(s2, &addr_p[i], sizeof(addr_p[i])) == SOCKET_ERROR) {
+#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(errno);
+       }
+
+       addrlen = sizeof(local_addr);
+
+       if (getsockname(s2, &local_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);
+       }
+
+       /* some brain-dead OS's don't return useful information from
+        * the getsockname call.  Namely, windows and solaris.  */
+
+       if (((struct sockaddr_in *)&local_addr)->sin_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);
+}
index 3f31216ddc1cbb33144a3a3951ab7a3c958ce5e8..7530e0966e3363b37f5246e964f92ad74efe219d 100644 (file)
 
 /*
  * 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 */
index 918a14f5e150e1bdef03200093e40fcbec1eea87..02c186f88dcd738f2df6f9f2219947d111c8651e 100644 (file)
@@ -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 (file)
index 0000000..10f9d54
--- /dev/null
@@ -0,0 +1,128 @@
+#include "k5-int.h"
+#if !defined(_MSDOS) && (!defined(_WIN32) || (defined(_WIN32) && defined(__CYGWIN32__))) && !defined(_MACINTOSH)
+#include <stdio.h>
+#include <errno.h>
+#include <signal.h>
+#include <setjmp.h>
+#ifdef __vxworks
+#define ECHO_PASSWORD
+#endif
+
+#ifndef ECHO_PASSWORD
+#include <termios.h>
+#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; i<num_prompts; i++) {
+#ifndef ECHO_PASSWORD
+       if (prompts[i].hidden) {
+           /* get the file descriptor associated with stdin */
+           fd = fileno(stdin);
+
+           if (isatty(fd) == 1) {
+               if (tcgetattr(fd, &echo_control) == -1)
+                   return errno;
+
+               save_control = echo_control;
+               echo_control.c_lflag &= ~(ECHO|ECHONL);
+
+               if (tcsetattr(fd, TCSANOW, &echo_control) == -1)
+                   return errno;
+           }
+       }
+#endif /* ECHO_PASSWORD */
+
+       /* put out the prompt */
+       (void) fputs(prompts[i].prompt,stdout);
+       (void) fputs(": ",stdout);
+       (void) fflush(stdout);
+       (void) memset(prompts[i].reply->data, 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 */
+   
index 881066d0809d6a2bbd18c7da18a0db477762ed20..ff14863b48f80f1e8151c0c4273a4b3fcb8129ee 100644 (file)
@@ -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 */