From d754fac0df327712a113c32e999716d3713486fb Mon Sep 17 00:00:00 2001 From: Ezra Peisach Date: Sun, 18 Aug 1996 22:32:27 +0000 Subject: [PATCH] Add the files to compile the old password changing program (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 | 7 + src/kadmin/v5passwdd/Makefile.in | 40 +- src/kadmin/v5passwdd/adm_conn.c | 762 +++++++++++++++++++++++++++++++ src/kadmin/v5passwdd/kpasswd.c | 582 +++++++++++++++++++++++ src/kadmin/v5passwdd/v5passwd.M | 62 +++ 5 files changed, 1445 insertions(+), 8 deletions(-) create mode 100644 src/kadmin/v5passwdd/adm_conn.c create mode 100644 src/kadmin/v5passwdd/kpasswd.c create mode 100644 src/kadmin/v5passwdd/v5passwd.M diff --git a/src/kadmin/v5passwdd/ChangeLog b/src/kadmin/v5passwdd/ChangeLog index 425781a3f..b511230a0 100644 --- a/src/kadmin/v5passwdd/ChangeLog +++ b/src/kadmin/v5passwdd/ChangeLog @@ -1,3 +1,10 @@ +Sun Aug 18 18:29:39 1996 Ezra Peisach + + * 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 * Makefile.in, configure.in, adm_rw.c, kadm5_defs.h, main.c, diff --git a/src/kadmin/v5passwdd/Makefile.in b/src/kadmin/v5passwdd/Makefile.in index a46a32686..54b9aa084 100644 --- a/src/kadmin/v5passwdd/Makefile.in +++ b/src/kadmin/v5passwdd/Makefile.in @@ -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 index 000000000..1534b4b02 --- /dev/null +++ b/src/kadmin/v5passwdd/adm_conn.c @@ -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 +#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 *)); + +/* + * 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); +} + +/* + * 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); +} + +/* + * 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 = [:[]] + */ + 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); +} + +/* + * 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); +} + +/* + * 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); + +} + +/* + * 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 index 000000000..f030b8e62 --- /dev/null +++ b/src/kadmin/v5passwdd/kpasswd.c @@ -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. + */ + +/* + * Include files. + */ +#include "k5-int.h" +#include "adm_defs.h" +#include "adm.h" + +#ifdef USE_STRING_H +#include +#else /* USE_STRING_H */ +#include +#endif /* USE_STRING_H */ + + +/* + * Local definitions. + */ +#define KPWD_MAX_TRIES 4 + +/* + * 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"; + +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 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 index 000000000..5c3bd32c9 --- /dev/null +++ b/src/kadmin/v5passwdd/v5passwd.M @@ -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 -- 2.26.2