+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,
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)
--- /dev/null
+/*
+ * 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);
+}
+
--- /dev/null
+/*
+ * 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);
+}
--- /dev/null
+.\" 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