Add the files to compile the old password changing program
authorEzra Peisach <epeisach@mit.edu>
Sun, 18 Aug 1996 22:32:27 +0000 (22:32 +0000)
committerEzra Peisach <epeisach@mit.edu>
Sun, 18 Aug 1996 22:32:27 +0000 (22:32 +0000)
(installed as v5passwd).

Comming soon: a test suite that uses this client.

git-svn-id: svn://anonsvn.mit.edu/krb5/trunk@8950 dc483132-0cff-0310-8789-dd5450dbe970

src/kadmin/v5passwdd/ChangeLog
src/kadmin/v5passwdd/Makefile.in
src/kadmin/v5passwdd/adm_conn.c [new file with mode: 0644]
src/kadmin/v5passwdd/kpasswd.c [new file with mode: 0644]
src/kadmin/v5passwdd/v5passwd.M [new file with mode: 0644]

index 425781a3f68b216bda102ce5d01eafedd959737d..b511230a04b337ace68402b0bf1e17a7f46d7bad 100644 (file)
@@ -1,3 +1,10 @@
+Sun Aug 18 18:29:39 1996  Ezra Peisach  <epeisach@kangaroo.mit.edu>
+
+       * Makefile.in: Compile v5passwd - the old passwd changing client. 
+
+       * adm_conn.c, kpasswd.c, v5passwd.M: Added the pieces required to
+               build a v5b6 simple password changing client. 
+
 Thu Jul 25 17:01:32 1996  Marc Horowitz  <marc@mit.edu>
 
        * Makefile.in, configure.in, adm_rw.c, kadm5_defs.h, main.c,
index a46a32686ec17ce40da3d8e76ada57b1545bd1f2..54b9aa08467608868947ac9e11c0cb78444a032c 100644 (file)
@@ -1,18 +1,42 @@
 CFLAGS = $(CCOPTS) $(DEFS) $(LOCALINCLUDE)
 
-SRCS = main.c srv_net.c proto_serv.c adm_rw.c
-OBJS = main.o srv_net.o proto_serv.o adm_rw.o
 
-PROG = v5passwdd
+COMMON_SRCS = adm_rw.c
+COMMON_OBJS = adm_rw.o
+SERV_SRCS = main.c srv_net.c proto_serv.c 
+SERV_OBJS = main.o srv_net.o proto_serv.o 
+CLI_SRCS = kpasswd.c adm_conn.c
+CLI_OBJS = kpasswd.o adm_conn.o
 
-all:: $(PROG)
+SRCS=$(SERV_SRCS) $(COMMON_SRCS) $(CLI_SRCS)
+OBJS=$(SERV_OBJS) $(COMMON_OBJS) $(CLI_OBJS)
 
-$(PROG): $(OBJS) $(DEPLIBS)
-       $(LD) $(LDFLAGS) $(LDARGS) -o $(PROG) $(OBJS) $(LIBS)
+SERV = v5passwdd
+CLI = v5passwd
+
+all:: $(SERV) $(CLI)
+
+$(SERV): $(SERV_OBJS) $(COMMON_OBJS) $(SRVDEPLIBS)
+       $(LD) $(LDFLAGS) $(LDARGS) -o $(SERV) $(SERV_OBJS) \
+               $(COMMON_OBJS) $(SRVLIBS)
+
+
+$(CLI): $(CLI_OBJS) $(COMMON_OBJS) $(CLNTDEPLIBS)
+       $(LD) $(LDFLAGS) $(LDARGS) -o $(CLI) $(CLI_OBJS) \
+               $(COMMON_OBJS) $(CLNTLIBS)
 
 install::
-       $(INSTALL_PROGRAM) $(C)$(PROG) ${DESTDIR}$(SERVER_BINDIR)$(S)$(PROG)
+       for f in v5passwd; do \
+         $(INSTALL_PROGRAM) $(C)$$f \
+               $(DESTDIR)$(CLIENT_BINDIR)$(S)`echo $$f|sed '$(transform)'`; \
+         $(INSTALL_DATA) $(srcdir)$(S)$$f.M \
+               ${DESTDIR}$(CLIENT_MANDIR)$(S)`echo $$f|sed '$(transform)'`.1; \
+       done
+       for f in v5passwdd; do \
+         $(INSTALL_PROGRAM) $(C)$$f \
+               $(DESTDIR)$(SERVER_BINDIR)$(S)`echo $$f|sed '$(transform)'`; \
+       done
 
 clean::
-       $(RM) $(PROG)
+       $(RM) $(SERV) $(CLI)
 
diff --git a/src/kadmin/v5passwdd/adm_conn.c b/src/kadmin/v5passwdd/adm_conn.c
new file mode 100644 (file)
index 0000000..1534b4b
--- /dev/null
@@ -0,0 +1,762 @@
+/*
+ * lib/kadm/adm_conn.c
+ *
+ * Copyright 1995 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.
+ *
+ */
+/*
+ * Routines to contact an administrative protocol server.
+ */
+#define        NEED_SOCKETS
+#define        NEED_LOWLEVEL_IO
+#include "k5-int.h"
+#include "adm.h"
+#include "adm_proto.h"
+
+#if    HAVE_PWD_H
+#include <pwd.h>
+#endif /* HAVE_PWD_H */
+
+/* Default ticket life is 10 minutes */
+#define        KADM_DEFAULT_LIFETIME   (10*60)
+
+/*
+ * Strings
+ */
+static char *kadm_cache_name_fmt =     "FILE:/tmp/tkt_kadm_%d";
+
+/*
+ * Prototypes for local functions
+ */
+static krb5_error_code kadm_get_ccache
+       PROTOTYPE((krb5_context,
+                  char *,
+                  char *,
+                  krb5_ccache *,
+                  krb5_principal *));
+static krb5_error_code kadm_get_creds
+       PROTOTYPE((krb5_context,
+               krb5_ccache ,
+               krb5_principal,
+               krb5_creds  *,
+               char *,
+               char *,
+               krb5_timestamp));
+static krb5_error_code kadm_contact_server
+       PROTOTYPE((krb5_context,
+               krb5_data *,
+               int *,
+               krb5_address **,
+               krb5_address **));
+static krb5_error_code kadm_get_auth
+       PROTOTYPE((krb5_context,
+               krb5_auth_context *,
+               krb5_address *,
+               krb5_address *));
+\f
+/*
+ * kadm_get_ccache()   - Initialze a credentials cache.
+ * 
+ * Cleanup after success by calling krb5_cc_destroy() and krb5_free_principal()
+ * Allocates new ccache and client.
+ */
+static krb5_error_code
+kadm_get_ccache(kcontext, user, ccname, ccache, client)
+    krb5_context       kcontext;
+    char               *user;
+    char               *ccname;
+    krb5_ccache                *ccache;
+    krb5_principal     *client;
+{
+    krb5_error_code    kret;
+    char               *name;
+    int                did_malloc = 0;
+    char               new_cache[MAXPATHLEN];
+    krb5_principal     tprinc;
+
+    /* Initialize. */
+    *client = (krb5_principal) NULL;
+    tprinc = (krb5_principal) NULL;
+
+    /*
+     * If a name specified, then use that one, else get it from our
+     * current uid.
+     */
+    if (user) {
+       name = user;
+    }
+    else {
+#if    HAVE_PWD_H
+       struct passwd *pw;
+
+       pw = getpwuid(getuid());
+       if (pw) {
+           name = (char *) malloc(strlen(pw->pw_name)+1);
+           did_malloc = 1;
+           strcpy(name, pw->pw_name);
+       }
+       else {
+           kret = errno;
+           goto cleanup;
+       }
+#else  /* HAVE_PWD_H */
+       kret = ENOENT;
+       goto cleanup;
+#endif /* HAVE_PWD_H */
+    }
+
+    /* Parse the name and form our principal */
+    if (kret = krb5_parse_name(kcontext, name, client))
+       goto cleanup;
+
+    if (!ccname) {
+#if defined(_MSDOS) || defined(_WIN32)
+       strcpy (new_cache, "FILE:");
+       GetTempFileName (0, "tkt", 0, new_cache+5);
+#else
+#ifdef _MACINTOSH
+       (void) sprintf(new_cache, "STDIO:admcc");
+#else
+       (void) sprintf(new_cache, kadm_cache_name_fmt, getpid());
+#endif /* _MACINTOSH */
+#endif /* _MSDOS || _WIN32 */
+    }
+    else
+       sprintf(new_cache, "FILE:%s", ccname);
+
+    /*
+     * We only need to resolve the credentials cache if one hasn't
+     * been supplied to us.
+     */
+    if (!(*ccache) && (kret = krb5_cc_resolve(kcontext, new_cache, ccache)))
+       goto cleanup;
+
+    /* XXX assumes a file ccache */
+    if ((kret = krb5_cc_get_principal(kcontext, *ccache, &tprinc)) ==
+       KRB5_FCC_NOFILE)
+       kret = krb5_cc_initialize(kcontext, *ccache, *client);
+
+
+ cleanup:
+    if (did_malloc)
+       free(name);
+
+    if (tprinc)
+       krb5_free_principal(kcontext, tprinc);
+
+    if (kret) {
+       if (*client)
+           krb5_free_principal(kcontext, *client);
+    }
+
+    return(kret);
+}
+\f
+/*
+ * kadm_get_creds()    - Get initial credentials.
+ *
+ * Cleanup after success by calling krb5_free_principal().
+ * Allocates new principal for creds->server.
+ */
+static krb5_error_code
+kadm_get_creds(kcontext, ccache, client, creds, prompt, oldpw, tlife)
+    krb5_context       kcontext;
+    krb5_ccache                ccache;
+    krb5_principal     client;
+    krb5_creds         *creds;
+    char               *prompt;
+    char               *oldpw;
+    krb5_timestamp     tlife;
+{
+    char               *client_name;
+    krb5_error_code    kret;
+    krb5_address       **my_addresses;
+    int                        old_pwsize;
+    krb5_creds         tcreds;
+
+    /* Initialize */
+    my_addresses = (krb5_address **) NULL;
+    client_name = (char *) NULL;
+
+    /* Get the string form for our principal */
+    if (kret = krb5_unparse_name(kcontext, client, &client_name))
+       return(kret);
+
+    if (kret = krb5_os_localaddr(kcontext, &my_addresses))
+       goto cleanup;
+
+    creds->client = client;
+    /*
+     * Build server principal name:
+     * "changepw" is service
+     * realm name is instance
+     * realm name is realm name
+     */
+    if (kret = krb5_build_principal_ext(kcontext,
+                                       &creds->server,
+                                       client->realm.length,
+                                       client->realm.data,
+                                       strlen(KRB5_ADM_SERVICE_INSTANCE),
+                                       KRB5_ADM_SERVICE_INSTANCE,
+                                       client->realm.length,
+                                       client->realm.data,
+                                       0))
+       goto cleanup;
+
+    /* Attempt to retrieve an appropriate entry from the credentials cache. */
+    if ((kret = krb5_cc_retrieve_cred(kcontext,
+                                     ccache,
+                                     KRB5_TC_MATCH_SRV_NAMEONLY,
+                                     creds,
+                                     &tcreds))
+       == KRB5_CC_NOTFOUND) {
+       krb5_timestamp  jetzt;
+
+       if (prompt != (char *) NULL) {
+           /* Read the password */
+           old_pwsize = KRB5_ADM_MAX_PASSWORD_LEN;
+           if (kret = krb5_read_password(kcontext,
+                                         prompt,
+                                         (char *) NULL,
+                                         oldpw,
+                                         &old_pwsize))
+               goto cleanup;
+       }
+       if (kret = krb5_timeofday(kcontext, &jetzt))
+           goto cleanup;
+       if (tlife > 0)
+           creds->times.endtime = jetzt + tlife;
+       else
+           creds->times.endtime = jetzt + KADM_DEFAULT_LIFETIME;
+
+       /* Get our initial ticket */
+       kret = krb5_get_in_tkt_with_password(kcontext,
+                                            0,
+                                            my_addresses,
+                                            NULL,
+                                            NULL,
+                                            oldpw,
+                                            ccache,
+                                            creds,
+                                            0);
+    }
+    else {
+       krb5_principal sclient, sserver;
+
+       if (!kret) {
+           /*
+            * We found the credentials cache entry - copy it out.
+            *
+            * We'd like to just blast tcreds on top of creds, but we cannot.
+            * other logic uses the client data, and rather than going and
+            * chasing all that logic down, might as well pretend that we just
+            * filled in all the other muck.
+            */
+           sclient = creds->client;
+           sserver = creds->server;
+           memcpy((char *) creds, (char *) &tcreds, sizeof(tcreds));
+           if (creds->client)
+               krb5_free_principal(kcontext, creds->client);
+           if (creds->server)
+               krb5_free_principal(kcontext, creds->server);
+           creds->client = sclient;
+           creds->server = sserver;
+       }
+    }
+
+ cleanup:
+    if (kret) {
+       if (creds->server) {
+           krb5_free_principal(kcontext, creds->server);
+           creds->server = 0;
+       }
+    }
+    if (my_addresses)
+       krb5_free_addresses(kcontext, my_addresses);
+    if (client_name)
+       krb5_xfree(client_name);
+    return(kret);
+}
+\f
+/*
+ * kadm_contact_server()       - Establish a connection to the server.
+ *
+ * Cleanup after success by calling close() and free().
+ * Opens/connects socket *sockp.  Allocates address storage for local/remote.
+ */
+static krb5_error_code
+kadm_contact_server(kcontext, realmp, sockp, local, remote)
+    krb5_context       kcontext;
+    krb5_data          *realmp;
+    int                        *sockp;
+    krb5_address       **local;
+    krb5_address       **remote;
+{
+    struct hostent     *remote_host;
+    struct servent     *service;
+    char               **hostlist;
+    int                        i, count;
+
+    krb5_error_code    kret;
+
+    struct sockaddr_in in_local;
+    struct sockaddr_in in_remote;
+    int                        addr_len;
+
+    const char         *realm_admin_names[4];
+    char               *realm_name;
+    krb5_boolean       found;
+
+    /* Initialize */
+    hostlist = (char **) NULL;
+    *sockp = -1;
+    realm_name = (char *) NULL;
+
+    /*
+     * XXX - only know ADDRTYPE_INET.
+     */
+#ifdef KRB5_USE_INET
+    *local = (krb5_address *) malloc(sizeof(krb5_address));
+    *remote = (krb5_address *) malloc(sizeof(krb5_address));
+    realm_name = (char *) malloc((size_t) realmp->length + 1);
+    if ((*local == (krb5_address *) NULL) ||
+       (*remote == (krb5_address *) NULL) ||
+       (realm_name == (char *) NULL)) {
+       kret = ENOMEM;
+       goto cleanup;
+    }
+    (*local)->addrtype = (*remote)->addrtype = ADDRTYPE_INET;
+    (*local)->length = (*remote)->length = sizeof(struct in_addr);
+    (*local)->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    (*remote)->contents = (krb5_octet *) malloc(sizeof(struct in_addr));
+    if (((*local)->contents == NULL) || ((*remote)->contents == NULL)) {
+       kret = ENOMEM;
+       goto cleanup;
+    }
+
+    /*
+     * First attempt to find addresses from our config file, if we cannot
+     * find an entry, then try getservbyname().
+     */
+    found = 0;
+#ifndef        OLD_CONFIG_FILES
+    strncpy(realm_name, realmp->data, (size_t) realmp->length);
+    realm_name[realmp->length] = '\0';
+    realm_admin_names[0] = "realms";
+    realm_admin_names[1] = realm_name;
+    realm_admin_names[2] = "admin_server";
+    realm_admin_names[3] = (char *) NULL;
+    if (!(kret = profile_get_values(kcontext->profile,
+                                   realm_admin_names,
+                                   &hostlist))) {
+       int             hi;
+       char            *cport;
+       char            *cp;
+       krb5_int32      pport;
+
+       for (hi = 0; hostlist[hi]; hi++) {
+           /*
+            * This knows a little too much about the format of profile
+            * entries.  Shouldn't it just be some sort of tuple?
+            *
+            * The form is assumed to be:
+            *  admin_server = <hostname>[:<portname>[<whitespace>]]
+            */
+           cport = (char *) NULL;
+           pport = (u_short) KRB5_ADM_DEFAULT_PORT;
+           cp = strchr(hostlist[hi], ' ');
+           if (cp)
+               *cp = '\0';
+           cp = strchr(hostlist[hi], '\t');
+           if (cp)
+               *cp = '\0';
+           cport = strchr(hostlist[hi], ':');
+           if (cport) {
+               *cport = '\0';
+               cport++;
+
+               pport = atoi (cport);
+               if (pport == 0) {
+                   kret = KRB5_CONFIG_BADFORMAT;
+                   goto cleanup;
+               }
+           }
+
+           /*
+            * Now that we have a host name, get the host entry.
+            */
+           remote_host = gethostbyname(hostlist[hi]);
+           if (remote_host == (struct hostent *) NULL) {
+               kret = KRB5_CONFIG_BADFORMAT;
+               goto cleanup;
+           }
+
+           /*
+            * Fill in our address values.
+            */
+           in_remote.sin_family = remote_host->h_addrtype;
+           (void) memcpy((char *) &in_remote.sin_addr,
+                         (char *) remote_host->h_addr,
+                         sizeof(in_remote.sin_addr));
+           in_remote.sin_port = htons((u_short) pport);
+
+           /* Open a tcp socket */
+           *sockp = (int) socket(PF_INET, SOCK_STREAM, 0);
+           if (*sockp < 0) {
+               kret = SOCKET_ERRNO;
+               goto cleanup;
+           }
+           else kret = 0;
+
+           /* Attempt to connect to the remote address. */
+           if (connect(*sockp,
+                       (struct sockaddr *) &in_remote,
+                       sizeof(in_remote)) < 0) {
+               /* Failed, go to next address */
+               kret = SOCKET_ERRNO;
+               closesocket((SOCKET)*sockp);
+               *sockp = -1;
+               continue;
+           }
+
+           /* Find out local address */
+           addr_len = sizeof(in_local);
+           if (getsockname((SOCKET) *sockp,
+                           (struct sockaddr *) &in_local,
+                           &addr_len) < 0) {
+               /* Couldn't get our local address? */
+               kret = SOCKET_ERRNO;
+               goto cleanup;
+           }
+           else {
+               /* Connection established. */
+               memcpy((char *) (*remote)->contents,
+                      (char *) &in_remote.sin_addr,
+                      sizeof(struct in_addr));
+               memcpy((char *) (*local)->contents,
+                      (char *) &in_local.sin_addr,
+                      sizeof(struct in_addr));
+               found = 1;
+               break;
+           }
+       }
+       if (!found) {
+           krb5_xfree(hostlist);
+           hostlist = (char **) NULL;
+       }
+    }
+#endif /* OLD_CONFIG_FILES */
+    if (!found) {
+       /*
+        * Use the old way of finding our administrative server.
+        *
+        * This consists of looking up an entry in /etc/services and if
+        * we don't find it, then we are just out of luck.  Then, we use
+        * that port number along with the address of the kdc.
+        */
+       if ((service = getservbyname(KRB5_ADM_SERVICE_NAME, "tcp")) == NULL) {
+           kret = ENOENT;
+           goto cleanup;
+       }
+       in_remote.sin_port = service->s_port;
+       
+       if (kret = krb5_get_krbhst(kcontext, realmp, &hostlist))
+           goto cleanup;
+       
+       /* Now count the number of hosts in the realm */
+       count = 0;
+       for (i=0; hostlist[i]; i++)
+           count++;
+       if (count == 0) {
+           kret = ENOENT;      /* something better? */
+           goto cleanup;
+       }
+       
+       /* Now find an available host */
+       for (i=0; hostlist[i]; i++) {
+           remote_host = gethostbyname(hostlist[i]);
+           if (remote_host != (struct hostent *) NULL) {
+               in_remote.sin_family = remote_host->h_addrtype;
+               (void) memcpy((char *) &in_remote.sin_addr,
+                             (char *) remote_host->h_addr,
+                             sizeof(in_remote.sin_addr));
+       
+               /* Open a tcp socket */
+               *sockp = (int) socket(PF_INET, SOCK_STREAM, 0);
+               if (*sockp < 0) {
+                   kret = SOCKET_ERRNO;
+                   goto cleanup;
+               }
+               else kret = 0;
+       
+               if (connect(*sockp,
+                           (struct sockaddr *) &in_remote,
+                           sizeof(in_remote)) < 0) {
+                   kret = SOCKET_ERRNO;
+                   closesocket((SOCKET)*sockp);
+                   *sockp = -1;
+                   continue;
+               }
+
+               /* Find out local address */
+               addr_len = sizeof(in_local);
+               if (getsockname((SOCKET)*sockp,
+                               (struct sockaddr *) &in_local,
+                               &addr_len) < 0) {
+                   kret = SOCKET_ERRNO;
+                   goto cleanup;
+               }
+               else {
+                   memcpy((char *) (*remote)->contents,
+                          (char *) &in_remote.sin_addr,
+                          sizeof(struct in_addr));
+       
+                   memcpy((char *) (*local)->contents,
+                          (char *) &in_local.sin_addr,
+                          sizeof(struct in_addr));
+                   found = 1;
+                   break;
+               }
+           }
+       }
+       if (!found)
+           kret = KRB5_SERVICE_UNKNOWN;
+    }
+#else  /* KRB5_USE_INET */
+    kret = ENOENT;
+#endif /* KRB5_USE_INET */
+
+ cleanup:
+    if (kret) {
+       if (*sockp >= 0)
+           closesocket((SOCKET)*sockp);
+       if (*local && (*local)->contents)
+           free((*local)->contents);
+       if (*remote && (*remote)->contents)
+           free((*remote)->contents);
+       if (*local) {
+           memset((char *) (*local), 0, sizeof(krb5_address));
+           free(*local);
+           *local = (krb5_address *) NULL;
+       }
+       if (*remote) {
+           memset((char *) (*remote), 0, sizeof(krb5_address));
+           free(*remote);
+           *remote = (krb5_address *) NULL;
+       }
+    }
+    if (realm_name)
+       free(realm_name);
+    if (hostlist)
+       krb5_xfree(hostlist);
+    return(kret);
+}
+\f
+/*
+ * kadm_get_auth()     - Get authorization context.
+ *
+ * Cleanup after success by calling krb5_xfree().
+ * New krb5_auth_context allocated in *ctxp
+ */
+static krb5_error_code
+kadm_get_auth(kcontext, ctxp, local, remote)
+    krb5_context       kcontext;
+    krb5_auth_context  *ctxp;
+    krb5_address       *local;
+    krb5_address       *remote;
+{
+    krb5_auth_con_init(kcontext, ctxp);
+    krb5_auth_con_setflags(kcontext, *ctxp, 
+                          KRB5_AUTH_CONTEXT_RET_SEQUENCE|
+                          KRB5_AUTH_CONTEXT_DO_SEQUENCE);
+    krb5_auth_con_setaddrs(kcontext, *ctxp, local, remote);
+    return(0);
+}
+\f
+/*
+ * krb5_adm_connect()  - Establish the connection to the service.
+ *
+ * If *ccachep is not null, then that ccache is used to establish the identity
+ * of the caller.  (Argument list is ugly, I know)
+ *
+ * Errors are not reported by this routine.
+ * Cleanup after successful invocation must:
+ *     destroy/close ccache.
+ *     free auth_context
+ *     close socket.
+ */
+krb5_error_code INTERFACE
+krb5_adm_connect(kcontext, user, prompt, opassword, sockp, ctxp,
+                ccachep, ccname, tlife)
+    krb5_context       kcontext;       /* Context handle       (In ) */
+    char               *user;          /* User specified       (In ) */
+    char               *prompt;        /* Old password prompt  (In ) */
+    char               *opassword;     /* Old Password         (I/O) */
+    int                        *sockp;         /* Socket for conn.     (Out) */
+    krb5_auth_context  *ctxp;          /* Auth context         (Out) */
+    krb5_ccache                *ccachep;       /* Credentials cache    (I/O) */
+    char               *ccname;        /* Cred cache name      (In ) */
+    krb5_timestamp     tlife;          /* Ticket lifetime      (In ) */
+{
+    krb5_error_code    kret;
+    krb5_principal     client;
+    krb5_creds         creds;
+    krb5_data          server_realm;
+    krb5_data          request_data, suppl_data;
+    krb5_data          response_data;
+    krb5_address       *local_addr;
+    krb5_address       *remote_addr;
+    krb5_boolean       ccache_supplied;
+
+    char               *server;
+
+    /* Initialize */
+    memset((char *) &creds, 0, sizeof(krb5_creds));
+    server = (char *) NULL;
+    *sockp = -1;
+    local_addr = remote_addr = (krb5_address *) NULL;
+    client = (krb5_principal) NULL;
+    *ctxp = (krb5_auth_context) NULL;
+    ccache_supplied = (*ccachep != (krb5_ccache) NULL);
+
+    /*
+     * Find the appropriate credentials cache and set up our identity.
+     */
+    if (kret = kadm_get_ccache(kcontext, user, ccname, ccachep, &client))
+       goto cleanup;
+
+    /*
+     * Get initial credentials.
+     */
+    if (kret = kadm_get_creds(kcontext,
+                             *ccachep,
+                             client,
+                             &creds,
+                             prompt,
+                             opassword,
+                             tlife))
+       goto cleanup;
+
+    /*
+     * Establish connection to server.
+     */
+    if ((server_realm.data = (char *) malloc(client->realm.length+1)) ==
+       (char *) NULL)
+       goto cleanup;
+
+    server_realm.length = client->realm.length;
+    memcpy(server_realm.data, client->realm.data, server_realm.length);
+    server_realm.data[server_realm.length] = '\0';
+    if (kret = kadm_contact_server(kcontext,
+                                  &server_realm,
+                                  sockp,
+                                  &local_addr,
+                                  &remote_addr))
+       goto cleanup;
+
+    /*
+     * Obtain our authorization context
+     */
+    if (kret = kadm_get_auth(kcontext, ctxp, local_addr, remote_addr))
+       goto cleanup;
+
+    /*
+     * Format, then send the KRB_AP_REQ
+     */
+    suppl_data.data = NULL;
+    suppl_data.length = 0;
+    if (kret = krb5_mk_req_extended(kcontext,
+                                   ctxp,
+                                   AP_OPTS_MUTUAL_REQUIRED,
+                                   &suppl_data,
+                                   &creds,
+                                   &request_data))
+       goto cleanup;
+
+    if (kret = krb5_write_message(kcontext, sockp, &request_data))
+       goto cleanup;
+
+    /*
+     * Now read back the response.
+     */
+    if (kret = krb5_read_message(kcontext, sockp, &response_data)) {
+       goto cleanup;
+    }
+    else {
+       krb5_ap_rep_enc_part    *reply = NULL;
+
+       kret = krb5_rd_rep(kcontext, *ctxp, &response_data, &reply);
+       if (reply)
+           krb5_free_ap_rep_enc_part(kcontext, reply);
+    }
+ cleanup:
+    if (server)
+       free(server);
+    if (kret) {
+       if (*ctxp) {
+           krb5_xfree(*ctxp);
+           *ctxp = (krb5_auth_context) NULL;
+       }
+       if (*sockp >= 0) {
+           closesocket((SOCKET)*sockp);
+           *sockp = -1;
+       }
+       if (local_addr && local_addr->contents)
+           free(local_addr->contents);
+       if (remote_addr && remote_addr->contents)
+           free(remote_addr->contents);
+       if (local_addr)
+           free(local_addr);
+       if (remote_addr)
+           free(remote_addr);
+       if (creds.server)
+           krb5_free_principal(kcontext, creds.server);
+       if (client)
+           krb5_free_principal(kcontext, client);
+       if (*ccachep && !ccache_supplied) {
+           krb5_cc_destroy(kcontext, *ccachep);
+           *ccachep = (krb5_ccache) NULL;
+       }
+    }
+    return(kret);
+
+}
+\f
+/*
+ * krb5_adm_disconnect()       - Disconnect from the administrative service.
+ *
+ * If ccache is supplied, then it is destroyed.  Otherwise, the ccache is
+ * the caller's responsibility to close.
+ */
+void INTERFACE
+krb5_adm_disconnect(kcontext, socketp, auth_context, ccache)
+    krb5_context       kcontext;
+    int                        *socketp;
+    krb5_auth_context  auth_context;
+    krb5_ccache                ccache;
+{
+    if (ccache)
+       krb5_cc_destroy(kcontext, ccache);
+    if (auth_context)
+       krb5_xfree(auth_context);
+    if (*socketp >= 0)
+       closesocket((SOCKET)*socketp);
+}
+
diff --git a/src/kadmin/v5passwdd/kpasswd.c b/src/kadmin/v5passwdd/kpasswd.c
new file mode 100644 (file)
index 0000000..f030b8e
--- /dev/null
@@ -0,0 +1,582 @@
+/*
+ * kadmin/kpasswd/kpasswd.c
+ *
+ * Copyright 1995 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.
+ *
+ */
+
+/*
+ * kpasswd
+ * change your password with Version 5 Kerberos using the new password
+ * changing protocol.
+ */
+\f
+/*
+ * Include files.
+ */
+#include "k5-int.h"
+#include "adm_defs.h"
+#include "adm.h"
+
+#ifdef USE_STRING_H
+#include <string.h>
+#else  /* USE_STRING_H */
+#include <strings.h>
+#endif /* USE_STRING_H */
+
+\f
+/*
+ * Local definitions.
+ */
+#define        KPWD_MAX_TRIES          4
+\f
+/*
+ * Local data.
+ */
+static const char *kpwd_serror_head =          "server";
+static const char *kpwd_change_prompt_1 =      "   Enter new password: ";
+static const char *kpwd_change_prompt_2 =      "Re-enter new password: ";
+static const char *kpwd_old_password_prompt =  "   Enter old password: ";
+static const char *kpwd_old_pwd_name_fmt =     "Enter old password for %s: ";
+
+#ifdef LANGUAGES_SUPPORTED
+static const char *kpwd_usage_error_fmt =      "%s: usage is %s [-u user] [-l language]\n";
+static const char *kpwd_getoptstring =         "l:u:";
+#else  /* LANGUAGES_SUPPORTED */
+static const char *kpwd_usage_error_fmt =      "%s: usage is %s [-u user]\n";
+static const char *kpwd_getoptstring =         "u:";
+#endif /* LANGUAGES_SUPPORTED */
+static const char *kpwd_extra_args =           "extra arguments";
+static const char *kpwd_bad_option_fmt =       "%s: unrecognized option -%c.\n";
+static const char *kpwd_no_memory_fmt =        "%s: not enough resources to allocate %d bytes for %s.\n";
+static const char *kpwd_bad_client_fmt =       "%s: %s%s%s %s not recognized by the server.\n";
+static const char *kpwd_no_server_fmt =                "%s: cannot find server for %s.\n";
+static const char *kpwd_incorrect_fmt =                "%s: incorrect password\n";
+static const char *kpwd_cant_connect_fmt =     "%s: cannot contact server (%s).\n";
+static const char *kpwd_proto_error_fmt =      "%s: protocol error during %s request (%s).\n";
+static const char *kpwd_pwproto_unsupp_fmt =   "%s: %s request not supported by server.\n";
+static const char *kpwd_pwproto_error =        "%s: server error (%s) during %s request.\n";
+static const char *kpwd_pwd_unacceptable =     "%s: your new password is unacceptable to the server, %s.\n";
+static const char *kpwd_read_pass_error =      "%s: error (%s) reading passwords.\n";
+
+static const char *kpwd_password_text = "passwords";
+static const char *kpwd_realm_text = "realm name";
+static const char *kpwd_args_text = "arguments";
+
+static const char *kpwd_try_again_text = "try again";
+static const char *kpwd_seeyalater_text = "password not changed";
+
+static const char *kpwd_mime_text = "MIME-enable";
+static const char *kpwd_language_text = "set language";
+static const char *kpwd_check_pwd_text = "check password";
+static const char *kpwd_change_pwd_text = "change password";
+static const char *kpwd_quit_text = "quit";
+
+static const char *kpwd_you = "you";
+static const char *kpwd_is_second = "are";
+static const char *kpwd_is_third = "is";
+static const char *kpwd_quote = "'";
+static const char *kpwd_null = "";
+
+static const char *kpwd_this_realm = "this realm";
+
+static const char *kpwd_replies[] = {
+    "Operation successful",            /* KRB5_ADM_SUCCESS */
+    "Command not recognized",          /* KRB5_ADM_CMD_UNKNOWN */
+    "Password unacceptable to server", /* KRB5_ADM_PW_UNACCEPT */
+    "Old password incorrect",          /* KRB5_ADM_BAD_PW */
+    "Invalid ticket (TKT_FLAG_INITIAL not set)",/* KRB5_ADM_NOT_IN_TKT */
+    "Server refused password change",  /* KRB5_ADM_CANT_CHANGE */
+    "Language not supported",          /* KRB5_ADM_LANG_NOT_SUPPORTED */
+};
+static const char *kpwd_replies_unknown = "UNKNOWN ERROR";
+\f
+static void
+usage(invocation, more_info)
+    char *invocation;
+    char *more_info;
+{
+    if (more_info)
+       fprintf(stderr, "%s: %s\n", invocation, more_info);
+    fprintf(stderr, kpwd_usage_error_fmt, invocation, invocation);
+}
+
+static const char *
+kpwd_reply_to_string(stat)
+    krb5_int32 stat;
+{
+    int        index;
+    const char *rval;
+
+    switch (stat) {
+    case KRB5_ADM_SUCCESS:
+    case KRB5_ADM_CMD_UNKNOWN:
+    case KRB5_ADM_PW_UNACCEPT:
+    case KRB5_ADM_BAD_PW:
+    case KRB5_ADM_NOT_IN_TKT:
+    case KRB5_ADM_CANT_CHANGE:
+    case KRB5_ADM_LANG_NOT_SUPPORTED:
+       index = (int) stat;
+       rval = kpwd_replies[index];
+       break;
+    default:
+       rval = kpwd_replies_unknown;
+       break;
+    }
+    return(rval);
+}
+
+static void
+kpwd_print_sreply(progname, ncomps, complist)
+    char       *progname;
+    krb5_int32 ncomps;
+    krb5_data  *complist;
+{
+    krb5_int32 i;
+    /*
+     * If language/mime suporrt enabled, need to have mime-decoder here.
+     */
+    if (ncomps > 0) {
+       fprintf(stderr, "%s - %s: %s\n", progname, kpwd_serror_head,
+               complist[0].data);
+       for (i=1; i<ncomps; i++)
+           fprintf(stderr, "\t%s\n", complist[i].data);
+    }
+}
+\f
+main(argc, argv)
+    int argc;
+    char *argv[];
+{
+    int                        option;
+    extern int         optind;
+    extern char                *optarg;
+    int                        error;
+
+    char               *name;
+    int                        mflag;
+    int                        lflag;
+    char               *language;
+
+    krb5_error_code    kret;
+    krb5_context       kcontext;
+    krb5_auth_context  auth_context;
+    krb5_ccache                ccache;
+    char               *opassword;
+    char               *npassword;
+    char               *opwd_prompt;
+
+    int                        conn_socket = -1;
+
+    int                        npass_tries;
+    int                        send_quit;
+
+    /*
+     * Initialize.
+     */
+    language = name = opwd_prompt = (char *) NULL;
+    mflag = lflag = error = 0;
+    send_quit = 0;
+    ccache = (krb5_ccache) NULL;
+
+    /*
+     * Usage is:
+     * kpasswd [-u user] [-l language]
+     */
+    while ((option = getopt(argc, argv, kpwd_getoptstring)) != EOF) {
+       switch (option) {
+       case 'u':
+           if ((name = (char *) malloc(strlen(optarg)+1)) == NULL) {
+               fprintf(stderr, kpwd_no_memory_fmt, argv[0], 
+                       strlen(optarg)+1, kpwd_args_text);
+               error = ENOMEM;
+               break;
+           }
+           strcpy(name, optarg);
+           break;
+#ifdef LANGUAGES_SUPPORTED
+       case 'l':
+           lflag++;
+           mflag++;
+           if ((language = (char *) malloc(strlen(optarg)+1)) == NULL) {
+               fprintf(stderr, kpwd_no_memory_fmt, argv[0], 
+                       strlen(optarg)+1, kpwd_args_text);
+               error = ENOMEM;
+               break;
+           }
+           strcpy(language, optarg);
+           break;
+#endif /* LANGUAGES_SUPPORTED */
+       default:
+           error++;
+           break;
+       }
+       if (error)
+           break;
+    }
+    if (error || ((argc - optind) > 0)) {
+       usage(argv[0], (error) ? (char *) NULL: kpwd_extra_args);
+       error++;
+       if (name)
+           free(name);
+       if (language)
+           free(language);
+       return(error);
+    }
+
+    /* Get space for passwords */
+    if (
+       ((npassword = (char *) malloc(KRB5_ADM_MAX_PASSWORD_LEN)) 
+       == (char *) NULL) ||
+       ((opassword = (char *) malloc(KRB5_ADM_MAX_PASSWORD_LEN)) 
+       == (char *) NULL)) {
+       fprintf(stderr, kpwd_no_memory_fmt, argv[0], KRB5_ADM_MAX_PASSWORD_LEN,
+               kpwd_password_text);
+       if (npassword)
+           free(npassword);
+       return(ENOMEM);
+    }
+
+    /*
+     * Initialize Kerberos
+     */
+    krb5_init_context(&kcontext);
+    krb5_init_ets(kcontext);
+
+    /* From now on, all error legs via 'goto cleanup' */
+
+    if (name) {
+       size_t prompt_len;
+
+       prompt_len = strlen(kpwd_old_pwd_name_fmt) - 2 + strlen(name) + 1;
+       opwd_prompt = (char *) malloc(prompt_len);
+       if (opwd_prompt)
+           sprintf(opwd_prompt, kpwd_old_pwd_name_fmt, name);
+    }
+    /*
+     * Establish the connection.
+     */
+    if (kret = krb5_adm_connect(kcontext,
+                               name,
+                               (opwd_prompt) ? 
+                                       opwd_prompt : kpwd_old_password_prompt,
+                               opassword,
+                               &conn_socket, 
+                               &auth_context,
+                               &ccache,
+                               (char *) NULL,
+                               0)) {
+       switch (kret) {
+       case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN:
+           fprintf(stderr, kpwd_bad_client_fmt, argv[0],
+                   (name) ? kpwd_quote : kpwd_null,
+                   (name) ? name : kpwd_you,
+                   (name) ? kpwd_quote : kpwd_null,
+                   (name) ? kpwd_is_third : kpwd_is_second);
+           break;
+       case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
+           fprintf(stderr, kpwd_no_server_fmt, argv[0],
+                   (name) ? name : kpwd_this_realm);
+           break;
+       case KRB5KRB_AP_ERR_BAD_INTEGRITY:
+           fprintf(stderr, kpwd_incorrect_fmt, argv[0]);
+           break;
+       default:
+           fprintf(stderr, kpwd_cant_connect_fmt, argv[0],
+                   error_message(kret));
+           break;
+       }
+       goto cleanup;
+    }
+
+    if (opwd_prompt)
+       free(opwd_prompt);
+    send_quit = 1;
+
+#ifdef LANGUAGES_SUPPORTED
+    /*
+     * We have the connection - see if we have to send some precursory data.
+     */
+    if (mflag) {
+       /*
+        * Need to engage in protocol for MIME setting
+        */
+       krb5_data       mime_data;
+       krb5_int32      mime_status;
+       krb5_int32      mime_ncomps;
+       krb5_data       *mime_reply;
+
+       mime_data.data = KRB5_ADM_MIME_CMD;
+       mime_data.length = strlen(mime_data.data);
+       if ((kret = krb5_send_adm_cmd(kcontext,
+                                     &conn_socket,
+                                     auth_context,
+                                     1,
+                                     &mime_data)) ||
+           (kret = krb5_read_adm_reply(kcontext,
+                                       &conn_socket,
+                                       auth_context,
+                                       &mime_status,
+                                       &mime_ncomps,
+                                       &mime_reply))) {
+           fprintf(stderr, kpwd_proto_error_fmt, argv[0], kpwd_mime_text,
+                   error_message(kret));
+           send_quit = 0;
+           goto cleanup;
+       }
+       switch (mime_status) {
+       case KRB5_ADM_SUCCESS:
+           break;
+       case KRB5_ADM_CMD_UNKNOWN:
+           fprintf(stderr, kpwd_pwproto_unsupp_fmt, argv[0], kpwd_mime_text);
+           if (mime_ncomps > 0)
+               kpwd_print_sreply(argv[0], mime_ncomps, mime_reply);
+           break;
+       default:
+           fprintf(stderr, kpwd_pwproto_error, argv[0],
+                   kpwd_reply_to_string(mime_status), kpwd_mime_text);
+           if (mime_ncomps > 0)
+               kpwd_print_sreply(argv[0], mime_ncomps, mime_reply);
+           goto cleanup;
+       }
+       krb5_free_adm_data(kcontext, mime_ncomps, mime_reply);
+    }
+    if (lflag && language) {
+       /*
+        * Need to engage in protocol for language setting
+        */
+       krb5_data       lang_data[2];
+       krb5_int32      lang_status;
+       krb5_int32      lang_ncomps;
+       krb5_data       *lang_reply;
+
+       lang_data[0].data = KRB5_ADM_LANGUAGE_CMD;
+       lang_data[0].length = strlen(lang_data[0].data);
+       lang_data[1].data = language;
+       lang_data[1].length = strlen(language);
+       if ((kret = krb5_send_adm_cmd(kcontext,
+                                     &conn_socket,
+                                     auth_context,
+                                     2,
+                                     lang_data)) ||
+           (kret = krb5_read_adm_reply(kcontext,
+                                       &conn_socket,
+                                       auth_context,
+                                       &lang_status,
+                                       &lang_ncomps,
+                                       &lang_reply))) {
+           fprintf(stderr, kpwd_proto_error_fmt, argv[0], kpwd_language_text,
+                   error_message(kret));
+           send_quit = 0;
+           goto cleanup;
+       }
+       switch (lang_status) {
+       case KRB5_ADM_SUCCESS:
+           break;
+       case KRB5_ADM_CMD_UNKNOWN:
+           fprintf(stderr, kpwd_pwproto_unsupp_fmt, argv[0],
+                   kpwd_language_text);
+           if (lang_ncomps > 0)
+               kpwd_print_sreply(argv[0], lang_ncomps, lang_reply);
+           break;
+       default:
+           fprintf(stderr, kpwd_pwproto_error, argv[0],
+                   kpwd_reply_to_string(lang_status), kpwd_language_text);
+           if (lang_ncomps > 0)
+               kpwd_print_sreply(argv[0], lang_ncomps, lang_reply);
+           goto cleanup;
+       }
+       krb5_free_adm_data(kcontext, lang_ncomps, lang_reply);
+    }
+#endif /* LANGUAGES_SUPPORTED */
+
+    /* Now - Actually change the password. */
+    for (npass_tries = 1; npass_tries <= KPWD_MAX_TRIES; npass_tries++) {
+       int npass_len;
+
+       npass_len = KRB5_ADM_MAX_PASSWORD_LEN;
+       if (!(kret = krb5_read_password(kcontext,
+                                       (char *) kpwd_change_prompt_1,
+                                       (char *) kpwd_change_prompt_2,
+                                       npassword,
+                                       &npass_len))) {
+           krb5_data           check_data[2];
+           krb5_int32          check_status;
+           krb5_int32          check_ncomps;
+           krb5_data           *check_reply;
+           krb5_data           set_data[3];
+           krb5_int32          set_status;
+           krb5_int32          set_ncomps;
+           krb5_int32          *set_reply;
+
+           check_data[0].data = KRB5_ADM_CHECKPW_CMD;
+           check_data[0].length = strlen(check_data[0].data);
+           check_data[1].data = npassword;
+           check_data[1].length = npass_len;
+           if ((kret = krb5_send_adm_cmd(kcontext,
+                                         &conn_socket,
+                                         auth_context,
+                                         2,
+                                         check_data)) ||
+               (kret = krb5_read_adm_reply(kcontext,
+                                           &conn_socket,
+                                           auth_context,
+                                           &check_status,
+                                           &check_ncomps,
+                                           &check_reply))) {
+               fprintf(stderr, kpwd_proto_error_fmt, argv[0], 
+                       kpwd_check_pwd_text, error_message(kret));
+               send_quit = 0;
+               error++;
+               break;
+           }
+           if ((check_status != KRB5_ADM_SUCCESS) &&
+               (check_status != KRB5_ADM_PW_UNACCEPT)) {
+               error++;
+               fprintf(stderr, kpwd_pwproto_error, argv[0],
+                       kpwd_reply_to_string(check_status),
+                       kpwd_check_pwd_text);
+               if (check_ncomps > 0)
+                   kpwd_print_sreply(argv[0], check_ncomps, check_reply);
+           }
+
+           if (check_status == KRB5_ADM_PW_UNACCEPT) {
+               fprintf(stderr, kpwd_pwd_unacceptable, argv[0],
+                       (npass_tries < KPWD_MAX_TRIES) ? 
+                       kpwd_try_again_text : kpwd_seeyalater_text);
+               if (check_ncomps > 0)
+                   kpwd_print_sreply(argv[0], check_ncomps, check_reply);
+               if (npass_tries == KPWD_MAX_TRIES)
+                   kret = check_status;
+               continue;
+           }
+           krb5_free_adm_data(kcontext, check_ncomps, check_reply);
+           if (error)
+               break;
+
+           /* Now actually change the password */
+           set_data[0].data = KRB5_ADM_CHANGEPW_CMD;
+           set_data[0].length = strlen(set_data[0].data);
+           set_data[1].data = opassword;
+           set_data[1].length = strlen(opassword);
+           set_data[2].data = npassword;
+           set_data[2].length = npass_len;
+           if ((kret = krb5_send_adm_cmd(kcontext,
+                                         &conn_socket,
+                                         auth_context,
+                                         3,
+                                         set_data)) ||
+               (kret = krb5_read_adm_reply(kcontext,
+                                           &conn_socket,
+                                           auth_context,
+                                           &set_status,
+                                           &set_ncomps,
+                                           &set_reply))) {
+               fprintf(stderr, kpwd_proto_error_fmt, argv[0], 
+                       kpwd_change_pwd_text, error_message(kret));
+               send_quit = 0;
+               error++;
+               break;
+           }
+           if (set_status != KRB5_ADM_SUCCESS) {
+               fprintf(stderr, kpwd_pwproto_error, argv[0],
+                       kpwd_reply_to_string(set_status),
+                       kpwd_change_pwd_text);
+               if (set_ncomps > 0)
+                   kpwd_print_sreply(argv[0], set_ncomps, set_reply);
+               error++;
+           }
+           krb5_free_adm_data(kcontext, set_ncomps, set_reply);
+           break;
+       }
+       else {
+           fprintf(stderr, kpwd_read_pass_error, argv[0],
+                   error_message(kret));
+           error++;
+           break;
+       }
+    }
+
+ cleanup:
+    if (kret)
+       error = kret;
+    if (language)
+       free(language);
+    if (name)
+       free(name);
+
+    /* Clear and free password storage */
+    if (opassword) {
+       memset(opassword, 0, KRB5_ADM_MAX_PASSWORD_LEN);
+       krb5_xfree(opassword);
+    }
+    if (npassword) {
+       memset(npassword, 0, KRB5_ADM_MAX_PASSWORD_LEN);
+       free(npassword);
+    }
+
+    if (send_quit) {
+       /*
+        * Need to send quit command.
+        */
+       krb5_data       quit_data;
+       krb5_int32      quit_status;
+       krb5_int32      quit_ncomps;
+       krb5_data       *quit_reply;
+       
+       quit_data.data = KRB5_ADM_QUIT_CMD;
+       quit_data.length = strlen(quit_data.data);
+       if ((kret = krb5_send_adm_cmd(kcontext,
+                                     &conn_socket,
+                                     auth_context,
+                                     1,
+                                     &quit_data)) ||
+           (kret = krb5_read_adm_reply(kcontext,
+                                       &conn_socket,
+                                       auth_context,
+                                       &quit_status,
+                                       &quit_ncomps,
+                                       &quit_reply))) {
+           fprintf(stderr, kpwd_proto_error_fmt, argv[0], kpwd_quit_text,
+                   error_message(kret));
+           goto done;
+       }
+       switch (quit_status) {
+       case KRB5_ADM_SUCCESS:
+           break;
+       case KRB5_ADM_CMD_UNKNOWN:
+           fprintf(stderr, kpwd_pwproto_unsupp_fmt, argv[0], kpwd_quit_text);
+           if (quit_ncomps > 0)
+               kpwd_print_sreply(argv[0], quit_ncomps, quit_reply);
+           break;
+       default:
+           fprintf(stderr, kpwd_pwproto_error, argv[0],
+                   kpwd_reply_to_string(quit_status), kpwd_quit_text);
+           if (quit_ncomps > 0)
+               kpwd_print_sreply(argv[0], quit_ncomps, quit_reply);
+       }
+       krb5_free_adm_data(kcontext, quit_ncomps, quit_reply);
+    }
+
+ done:
+    krb5_adm_disconnect(kcontext, &conn_socket,        auth_context, ccache);
+    krb5_xfree(kcontext);
+    return(error);
+}
diff --git a/src/kadmin/v5passwdd/v5passwd.M b/src/kadmin/v5passwdd/v5passwd.M
new file mode 100644 (file)
index 0000000..5c3bd32
--- /dev/null
@@ -0,0 +1,62 @@
+.\" kadmin/v5passwdd/v5passwd.M
+.\" 
+.\" Copyright 1995 by the Massachusetts Institute of Technology.
+.\"
+.\" 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.
+.\" 
+.\"
+.TH V5PASSWD 1 "Kerberos Version 5.0" "MIT Project Athena"
+.SH NAME
+v5passwd \- change a user's Kerberos password
+.SH SYNOPSIS
+.B v5passwd
+[
+.B \-u
+.IR username[/instance][@realm]
+]
+.SH DESCRIPTION
+The 
+.I v5passwd
+command is used to change a Kerberos principal's password using the
+older administrative protocol. 
+
+If the
+.I \-u
+option is specified, a partially or fully qualified Kerberos principal may
+be supplied.  Otherwise, the principal name is derived from the identity
+of the user invoking the
+.I v5passwd
+command.
+
+.PP
+The
+.I v5passwd
+command prompts for the current Kerberos password,  which is verified by the
+Kerberos server.  If the old password is correct, the user is prompted twice
+for the new password.  Success or failure is indicated by messages printed
+out by
+.I v5passwd.
+
+
+.SH FILES
+.TP 2i
+/tmp/tkt_kpw_[uid]
+the temporary credentials cache ([uid] is the decimal UID of the user) for
+the lifetime of the password changing operation.
+.SH SEE ALSO
+kadmin(8), kadmind(8), v5passwdd(8)
+.SH BUGS