10Sstevel@tonic-gate /*
2*11493SMark.Phalan@Sun.COM * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
30Sstevel@tonic-gate * Use is subject to license terms.
40Sstevel@tonic-gate */
50Sstevel@tonic-gate
60Sstevel@tonic-gate /*
70Sstevel@tonic-gate * lib/krb5/kadm5/srv/chgpwd.c
80Sstevel@tonic-gate *
90Sstevel@tonic-gate * Copyright 1998 by the Massachusetts Institute of Technology.
100Sstevel@tonic-gate * All Rights Reserved.
110Sstevel@tonic-gate *
120Sstevel@tonic-gate * Export of this software from the United States of America may
130Sstevel@tonic-gate * require a specific license from the United States Government.
140Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating
150Sstevel@tonic-gate * export to obtain such a license before exporting.
160Sstevel@tonic-gate *
170Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
180Sstevel@tonic-gate * distribute this software and its documentation for any purpose and
190Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright
200Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and
210Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that
220Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining
230Sstevel@tonic-gate * to distribution of the software without specific, written prior
240Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label
250Sstevel@tonic-gate * your software as modified software and not distribute it in such a
260Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software.
270Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of
280Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express
290Sstevel@tonic-gate * or implied warranty.
300Sstevel@tonic-gate *
310Sstevel@tonic-gate */
320Sstevel@tonic-gate
330Sstevel@tonic-gate /*
340Sstevel@tonic-gate * chgpwd.c - Handles changepw requests issued from non-Solaris krb5 clients.
350Sstevel@tonic-gate */
360Sstevel@tonic-gate
370Sstevel@tonic-gate #include <libintl.h>
380Sstevel@tonic-gate #include <locale.h>
390Sstevel@tonic-gate #include <kadm5/admin.h>
400Sstevel@tonic-gate #include <syslog.h>
410Sstevel@tonic-gate #include <krb5/adm_proto.h>
420Sstevel@tonic-gate
430Sstevel@tonic-gate #define MAXAPREQ 1500
440Sstevel@tonic-gate
450Sstevel@tonic-gate static krb5_error_code
process_chpw_request(krb5_context context,void * server_handle,char * realm,int s,krb5_keytab keytab,struct sockaddr_in * sin,krb5_data * req,krb5_data * rep)460Sstevel@tonic-gate process_chpw_request(krb5_context context, void *server_handle,
470Sstevel@tonic-gate char *realm, int s, krb5_keytab keytab,
480Sstevel@tonic-gate struct sockaddr_in *sin, krb5_data *req,
490Sstevel@tonic-gate krb5_data *rep)
500Sstevel@tonic-gate {
510Sstevel@tonic-gate krb5_error_code ret;
520Sstevel@tonic-gate char *ptr;
530Sstevel@tonic-gate int plen, vno;
540Sstevel@tonic-gate krb5_address local_kaddr, remote_kaddr;
550Sstevel@tonic-gate int allocated_mem = 0;
560Sstevel@tonic-gate krb5_data ap_req, ap_rep;
570Sstevel@tonic-gate krb5_auth_context auth_context;
580Sstevel@tonic-gate krb5_principal changepw;
590Sstevel@tonic-gate krb5_ticket *ticket;
600Sstevel@tonic-gate krb5_data cipher, clear;
610Sstevel@tonic-gate struct sockaddr local_addr, remote_addr;
620Sstevel@tonic-gate int addrlen;
630Sstevel@tonic-gate krb5_replay_data replay;
640Sstevel@tonic-gate krb5_error krberror;
650Sstevel@tonic-gate int numresult;
660Sstevel@tonic-gate char strresult[1024];
672941Ssemery char *clientstr;
683998Ssemery size_t clen;
693998Ssemery char *cdots;
700Sstevel@tonic-gate
710Sstevel@tonic-gate ret = 0;
720Sstevel@tonic-gate rep->length = 0;
730Sstevel@tonic-gate
740Sstevel@tonic-gate auth_context = NULL;
750Sstevel@tonic-gate changepw = NULL;
760Sstevel@tonic-gate ap_rep.length = 0;
770Sstevel@tonic-gate ap_rep.data = NULL;
780Sstevel@tonic-gate ticket = NULL;
790Sstevel@tonic-gate clear.length = 0;
800Sstevel@tonic-gate clear.data = NULL;
810Sstevel@tonic-gate cipher.length = 0;
820Sstevel@tonic-gate cipher.data = NULL;
830Sstevel@tonic-gate
840Sstevel@tonic-gate if (req->length < 4) {
850Sstevel@tonic-gate /*
860Sstevel@tonic-gate * either this, or the server is printing bad messages,
870Sstevel@tonic-gate * or the caller passed in garbage
880Sstevel@tonic-gate */
890Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED;
900Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED;
910Sstevel@tonic-gate (void) strlcpy(strresult, "Request was truncated",
920Sstevel@tonic-gate sizeof (strresult));
930Sstevel@tonic-gate goto chpwfail;
940Sstevel@tonic-gate }
950Sstevel@tonic-gate
960Sstevel@tonic-gate ptr = req->data;
970Sstevel@tonic-gate
980Sstevel@tonic-gate /*
990Sstevel@tonic-gate * Verify length
1000Sstevel@tonic-gate */
1010Sstevel@tonic-gate plen = (*ptr++ & 0xff);
1020Sstevel@tonic-gate plen = (plen<<8) | (*ptr++ & 0xff);
1030Sstevel@tonic-gate
1040Sstevel@tonic-gate if (plen != req->length)
1050Sstevel@tonic-gate return (KRB5KRB_AP_ERR_MODIFIED);
1060Sstevel@tonic-gate
1070Sstevel@tonic-gate /*
1080Sstevel@tonic-gate * Verify version number
1090Sstevel@tonic-gate */
1100Sstevel@tonic-gate vno = (*ptr++ & 0xff);
1110Sstevel@tonic-gate vno = (vno<<8) | (*ptr++ & 0xff);
1120Sstevel@tonic-gate
1130Sstevel@tonic-gate if (vno != 1) {
1140Sstevel@tonic-gate ret = KRB5KDC_ERR_BAD_PVNO;
1150Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED;
1160Sstevel@tonic-gate (void) snprintf(strresult, sizeof (strresult),
1170Sstevel@tonic-gate "Request contained unknown protocol version number %d",
1180Sstevel@tonic-gate vno);
1190Sstevel@tonic-gate goto chpwfail;
1200Sstevel@tonic-gate }
1210Sstevel@tonic-gate
1220Sstevel@tonic-gate /*
1230Sstevel@tonic-gate * Read, check ap-req length
1240Sstevel@tonic-gate */
1250Sstevel@tonic-gate ap_req.length = (*ptr++ & 0xff);
1260Sstevel@tonic-gate ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff);
1270Sstevel@tonic-gate
1280Sstevel@tonic-gate if (ptr + ap_req.length >= req->data + req->length) {
1290Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED;
1300Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED;
1310Sstevel@tonic-gate (void) strlcpy(strresult, "Request was truncated in AP-REQ",
1320Sstevel@tonic-gate sizeof (strresult));
1330Sstevel@tonic-gate goto chpwfail;
1340Sstevel@tonic-gate }
1350Sstevel@tonic-gate
1360Sstevel@tonic-gate /*
1370Sstevel@tonic-gate * Verify ap_req
1380Sstevel@tonic-gate */
1390Sstevel@tonic-gate ap_req.data = ptr;
1400Sstevel@tonic-gate ptr += ap_req.length;
1410Sstevel@tonic-gate
1420Sstevel@tonic-gate if (ret = krb5_auth_con_init(context, &auth_context)) {
1431474Smp153739 krb5_klog_syslog(LOG_ERR,
1441474Smp153739 gettext("Change password request failed. "
1451476Smp153739 "Failed initializing auth context: %s"),
1461474Smp153739 error_message(ret));
1470Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
1480Sstevel@tonic-gate (void) strlcpy(strresult, "Failed initializing auth context",
1490Sstevel@tonic-gate sizeof (strresult));
1500Sstevel@tonic-gate goto chpwfail;
1510Sstevel@tonic-gate }
1520Sstevel@tonic-gate
1530Sstevel@tonic-gate if (ret = krb5_auth_con_setflags(context, auth_context,
1540Sstevel@tonic-gate KRB5_AUTH_CONTEXT_DO_SEQUENCE)) {
1551474Smp153739 krb5_klog_syslog(LOG_ERR,
1561474Smp153739 gettext("Change password request failed. "
1571474Smp153739 "Failed setting auth "
1581474Smp153739 "context flags: %s"),
1591474Smp153739 error_message(ret));
1600Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
1610Sstevel@tonic-gate (void) strlcpy(strresult, "Failed initializing auth context",
1620Sstevel@tonic-gate sizeof (strresult));
1630Sstevel@tonic-gate goto chpwfail;
1640Sstevel@tonic-gate }
1650Sstevel@tonic-gate
1660Sstevel@tonic-gate if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm,
1670Sstevel@tonic-gate "kadmin", "changepw", NULL)) {
1681474Smp153739 krb5_klog_syslog(LOG_ERR,
1691474Smp153739 gettext("Change password request failed "
1701474Smp153739 "Failed to build kadmin/changepw "
1711474Smp153739 "principal: %s"),
1721474Smp153739 error_message(ret));
1730Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
1740Sstevel@tonic-gate (void) strlcpy(strresult,
1750Sstevel@tonic-gate "Failed building kadmin/changepw principal",
1760Sstevel@tonic-gate sizeof (strresult));
1770Sstevel@tonic-gate goto chpwfail;
1780Sstevel@tonic-gate }
1790Sstevel@tonic-gate
1800Sstevel@tonic-gate ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab,
1810Sstevel@tonic-gate NULL, &ticket);
1820Sstevel@tonic-gate
1830Sstevel@tonic-gate if (ret) {
1841474Smp153739 char kt_name[MAX_KEYTAB_NAME_LEN];
1851474Smp153739 if (krb5_kt_get_name(context, keytab,
1861474Smp153739 kt_name, sizeof (kt_name)))
1871474Smp153739 strncpy(kt_name, "default keytab", sizeof (kt_name));
1881474Smp153739
1891474Smp153739 switch (ret) {
1901474Smp153739 case KRB5_KT_NOTFOUND:
1911474Smp153739 krb5_klog_syslog(LOG_ERR,
1921474Smp153739 gettext("Change password request failed because "
1931474Smp153739 "keytab entry \"kadmin/changepw\" "
1941474Smp153739 "is missing from \"%s\""),
1951474Smp153739 kt_name);
1961474Smp153739 break;
1971474Smp153739 case ENOENT:
1981474Smp153739 krb5_klog_syslog(LOG_ERR,
1991474Smp153739 gettext("Change password request failed because "
2001474Smp153739 "keytab file \"%s\" does not exist"),
2011474Smp153739 kt_name);
2021474Smp153739 break;
2031474Smp153739 default:
2041474Smp153739 krb5_klog_syslog(LOG_ERR,
2051474Smp153739 gettext("Change password request failed. "
2061476Smp153739 "Failed to parse Kerberos AP_REQ message: %s"),
2071474Smp153739 error_message(ret));
2081474Smp153739 }
2091474Smp153739
2100Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR;
2110Sstevel@tonic-gate (void) strlcpy(strresult, "Failed reading application request",
2120Sstevel@tonic-gate sizeof (strresult));
2130Sstevel@tonic-gate goto chpwfail;
2140Sstevel@tonic-gate }
2150Sstevel@tonic-gate
2160Sstevel@tonic-gate /*
2170Sstevel@tonic-gate * Set up address info
2180Sstevel@tonic-gate */
2190Sstevel@tonic-gate addrlen = sizeof (local_addr);
2200Sstevel@tonic-gate
2210Sstevel@tonic-gate if (getsockname(s, &local_addr, &addrlen) < 0) {
2220Sstevel@tonic-gate ret = errno;
2230Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
2240Sstevel@tonic-gate (void) strlcpy(strresult,
2250Sstevel@tonic-gate "Failed getting server internet address",
2260Sstevel@tonic-gate sizeof (strresult));
2270Sstevel@tonic-gate goto chpwfail;
2280Sstevel@tonic-gate }
2290Sstevel@tonic-gate
2300Sstevel@tonic-gate /*
2310Sstevel@tonic-gate * Some brain-dead OS's don't return useful information from
2320Sstevel@tonic-gate * the getsockname call. Namely, windows and solaris.
2330Sstevel@tonic-gate */
2340Sstevel@tonic-gate if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) {
2350Sstevel@tonic-gate local_kaddr.addrtype = ADDRTYPE_INET;
2360Sstevel@tonic-gate local_kaddr.length = sizeof (((struct sockaddr_in *)
2370Sstevel@tonic-gate &local_addr)->sin_addr);
2380Sstevel@tonic-gate /* CSTYLED */
2390Sstevel@tonic-gate local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr);
2400Sstevel@tonic-gate } else {
2410Sstevel@tonic-gate krb5_address **addrs;
2420Sstevel@tonic-gate
2430Sstevel@tonic-gate krb5_os_localaddr(context, &addrs);
2440Sstevel@tonic-gate
2450Sstevel@tonic-gate local_kaddr.magic = addrs[0]->magic;
2460Sstevel@tonic-gate local_kaddr.addrtype = addrs[0]->addrtype;
2470Sstevel@tonic-gate local_kaddr.length = addrs[0]->length;
2480Sstevel@tonic-gate if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) {
2490Sstevel@tonic-gate ret = errno;
2500Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
2510Sstevel@tonic-gate (void) strlcpy(strresult,
2520Sstevel@tonic-gate "Malloc failed for local_kaddr",
2530Sstevel@tonic-gate sizeof (strresult));
2540Sstevel@tonic-gate goto chpwfail;
2550Sstevel@tonic-gate }
2560Sstevel@tonic-gate
2570Sstevel@tonic-gate (void) memcpy(local_kaddr.contents, addrs[0]->contents,
2580Sstevel@tonic-gate addrs[0]->length);
2590Sstevel@tonic-gate allocated_mem++;
2600Sstevel@tonic-gate
2610Sstevel@tonic-gate krb5_free_addresses(context, addrs);
2620Sstevel@tonic-gate }
2630Sstevel@tonic-gate
2640Sstevel@tonic-gate addrlen = sizeof (remote_addr);
2650Sstevel@tonic-gate
2660Sstevel@tonic-gate if (getpeername(s, &remote_addr, &addrlen) < 0) {
2670Sstevel@tonic-gate ret = errno;
2680Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
2690Sstevel@tonic-gate (void) strlcpy(strresult,
2700Sstevel@tonic-gate "Failed getting client internet address",
2710Sstevel@tonic-gate sizeof (strresult));
2720Sstevel@tonic-gate goto chpwfail;
2730Sstevel@tonic-gate }
2740Sstevel@tonic-gate
2750Sstevel@tonic-gate remote_kaddr.addrtype = ADDRTYPE_INET;
2760Sstevel@tonic-gate remote_kaddr.length = sizeof (((struct sockaddr_in *)
2770Sstevel@tonic-gate &remote_addr)->sin_addr);
2780Sstevel@tonic-gate /* CSTYLED */
2790Sstevel@tonic-gate remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr);
2800Sstevel@tonic-gate remote_kaddr.addrtype = ADDRTYPE_INET;
2810Sstevel@tonic-gate remote_kaddr.length = sizeof (sin->sin_addr);
2820Sstevel@tonic-gate remote_kaddr.contents = (krb5_octet *) &sin->sin_addr;
2830Sstevel@tonic-gate
2840Sstevel@tonic-gate /*
2850Sstevel@tonic-gate * mk_priv requires that the local address be set.
2860Sstevel@tonic-gate * getsockname is used for this. rd_priv requires that the
2870Sstevel@tonic-gate * remote address be set. recvfrom is used for this. If
2880Sstevel@tonic-gate * rd_priv is given a local address, and the message has the
2890Sstevel@tonic-gate * recipient addr in it, this will be checked. However, there
2900Sstevel@tonic-gate * is simply no way to know ahead of time what address the
2910Sstevel@tonic-gate * message will be delivered *to*. Therefore, it is important
2920Sstevel@tonic-gate * that either no recipient address is in the messages when
2930Sstevel@tonic-gate * mk_priv is called, or that no local address is passed to
2940Sstevel@tonic-gate * rd_priv. Both is a better idea, and I have done that. In
2950Sstevel@tonic-gate * summary, when mk_priv is called, *only* a local address is
2960Sstevel@tonic-gate * specified. when rd_priv is called, *only* a remote address
2970Sstevel@tonic-gate * is specified. Are we having fun yet?
2980Sstevel@tonic-gate */
2990Sstevel@tonic-gate if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL,
3000Sstevel@tonic-gate &remote_kaddr)) {
3010Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
3020Sstevel@tonic-gate (void) strlcpy(strresult,
3030Sstevel@tonic-gate "Failed storing client internet address",
3040Sstevel@tonic-gate sizeof (strresult));
3050Sstevel@tonic-gate goto chpwfail;
3060Sstevel@tonic-gate }
3070Sstevel@tonic-gate
3080Sstevel@tonic-gate /*
3090Sstevel@tonic-gate * Verify that this is an AS_REQ ticket
3100Sstevel@tonic-gate */
3110Sstevel@tonic-gate if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) {
3120Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR;
3130Sstevel@tonic-gate (void) strlcpy(strresult,
3140Sstevel@tonic-gate "Ticket must be derived from a password",
3150Sstevel@tonic-gate sizeof (strresult));
3160Sstevel@tonic-gate goto chpwfail;
3170Sstevel@tonic-gate }
3180Sstevel@tonic-gate
3190Sstevel@tonic-gate /*
3200Sstevel@tonic-gate * Construct the ap-rep
3210Sstevel@tonic-gate */
3220Sstevel@tonic-gate if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) {
3230Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR;
3240Sstevel@tonic-gate (void) strlcpy(strresult,
3250Sstevel@tonic-gate "Failed replying to application request",
3260Sstevel@tonic-gate sizeof (strresult));
3270Sstevel@tonic-gate goto chpwfail;
3280Sstevel@tonic-gate }
3290Sstevel@tonic-gate
3300Sstevel@tonic-gate /*
3310Sstevel@tonic-gate * Decrypt the new password
3320Sstevel@tonic-gate */
3330Sstevel@tonic-gate cipher.length = (req->data + req->length) - ptr;
3340Sstevel@tonic-gate cipher.data = ptr;
3350Sstevel@tonic-gate
3360Sstevel@tonic-gate if (ret = krb5_rd_priv(context, auth_context, &cipher,
3370Sstevel@tonic-gate &clear, &replay)) {
3380Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
3390Sstevel@tonic-gate (void) strlcpy(strresult, "Failed decrypting request",
3400Sstevel@tonic-gate sizeof (strresult));
3410Sstevel@tonic-gate goto chpwfail;
3420Sstevel@tonic-gate }
3430Sstevel@tonic-gate
3442941Ssemery ret = krb5_unparse_name(context, ticket->enc_part2->client, &clientstr);
3452941Ssemery if (ret) {
3462941Ssemery numresult = KRB5_KPASSWD_HARDERROR;
3472941Ssemery (void) strcpy(strresult, "Failed decrypting request");
3482941Ssemery goto chpwfail;
3492941Ssemery }
3502941Ssemery
3510Sstevel@tonic-gate /*
3520Sstevel@tonic-gate * Change the password
3530Sstevel@tonic-gate */
3540Sstevel@tonic-gate if ((ptr = (char *)malloc(clear.length + 1)) == NULL) {
3550Sstevel@tonic-gate ret = errno;
3560Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
3570Sstevel@tonic-gate (void) strlcpy(strresult, "Malloc failed for ptr",
3580Sstevel@tonic-gate sizeof (strresult));
3592941Ssemery krb5_free_unparsed_name(context, clientstr);
3600Sstevel@tonic-gate goto chpwfail;
3610Sstevel@tonic-gate }
3620Sstevel@tonic-gate (void) memcpy(ptr, clear.data, clear.length);
3630Sstevel@tonic-gate ptr[clear.length] = '\0';
3640Sstevel@tonic-gate
3650Sstevel@tonic-gate ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle,
3660Sstevel@tonic-gate ticket->enc_part2->client,
3670Sstevel@tonic-gate ptr, NULL, strresult,
3680Sstevel@tonic-gate sizeof (strresult));
3692941Ssemery
3700Sstevel@tonic-gate /*
3710Sstevel@tonic-gate * Zap the password
3720Sstevel@tonic-gate */
3730Sstevel@tonic-gate (void) memset(clear.data, 0, clear.length);
3740Sstevel@tonic-gate (void) memset(ptr, 0, clear.length);
3750Sstevel@tonic-gate if (clear.data != NULL) {
3760Sstevel@tonic-gate krb5_xfree(clear.data);
3770Sstevel@tonic-gate clear.data = NULL;
3780Sstevel@tonic-gate }
3790Sstevel@tonic-gate free(ptr);
3800Sstevel@tonic-gate clear.length = 0;
3810Sstevel@tonic-gate
3823998Ssemery clen = strlen(clientstr);
3833998Ssemery trunc_name(&clen, &cdots);
3843998Ssemery krb5_klog_syslog(LOG_NOTICE, "chpw request from %s for %.*s%s: %s",
3852941Ssemery inet_ntoa(((struct sockaddr_in *)&remote_addr)->sin_addr),
3863998Ssemery clen, clientstr, cdots, ret ? error_message(ret) : "success");
3872941Ssemery krb5_free_unparsed_name(context, clientstr);
3882941Ssemery
3890Sstevel@tonic-gate if (ret) {
3900Sstevel@tonic-gate if ((ret != KADM5_PASS_Q_TOOSHORT) &&
3910Sstevel@tonic-gate (ret != KADM5_PASS_REUSE) &&
3920Sstevel@tonic-gate (ret != KADM5_PASS_Q_CLASS) &&
3930Sstevel@tonic-gate (ret != KADM5_PASS_Q_DICT) &&
3940Sstevel@tonic-gate (ret != KADM5_PASS_TOOSOON))
3950Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
3960Sstevel@tonic-gate else
3970Sstevel@tonic-gate numresult = KRB5_KPASSWD_SOFTERROR;
3980Sstevel@tonic-gate /*
3990Sstevel@tonic-gate * strresult set by kadb5_chpass_principal_util()
4000Sstevel@tonic-gate */
4010Sstevel@tonic-gate goto chpwfail;
4020Sstevel@tonic-gate }
4030Sstevel@tonic-gate
4040Sstevel@tonic-gate /*
4050Sstevel@tonic-gate * Success!
4060Sstevel@tonic-gate */
4070Sstevel@tonic-gate numresult = KRB5_KPASSWD_SUCCESS;
4080Sstevel@tonic-gate (void) strlcpy(strresult, "", sizeof (strresult));
4090Sstevel@tonic-gate
4100Sstevel@tonic-gate chpwfail:
4110Sstevel@tonic-gate
4120Sstevel@tonic-gate clear.length = 2 + strlen(strresult);
4130Sstevel@tonic-gate if (clear.data != NULL) {
4140Sstevel@tonic-gate krb5_xfree(clear.data);
4150Sstevel@tonic-gate clear.data = NULL;
4160Sstevel@tonic-gate }
4170Sstevel@tonic-gate if ((clear.data = (char *)malloc(clear.length)) == NULL) {
4180Sstevel@tonic-gate ret = errno;
4190Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
4200Sstevel@tonic-gate (void) strlcpy(strresult, "Malloc failed for clear.data",
4210Sstevel@tonic-gate sizeof (strresult));
4220Sstevel@tonic-gate }
4230Sstevel@tonic-gate
4242941Ssemery ptr = clear.data;
4252941Ssemery
4262941Ssemery *ptr++ = (numresult>>8) & 0xff;
4272941Ssemery *ptr++ = numresult & 0xff;
4282941Ssemery
4292941Ssemery (void) memcpy(ptr, strresult, strlen(strresult));
4302941Ssemery
4310Sstevel@tonic-gate cipher.length = 0;
4320Sstevel@tonic-gate
4330Sstevel@tonic-gate if (ap_rep.length) {
4340Sstevel@tonic-gate if (ret = krb5_auth_con_setaddrs(context, auth_context,
4350Sstevel@tonic-gate &local_kaddr, NULL)) {
4360Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
4370Sstevel@tonic-gate (void) strlcpy(strresult,
4380Sstevel@tonic-gate "Failed storing client and server internet addresses",
4390Sstevel@tonic-gate sizeof (strresult));
4400Sstevel@tonic-gate } else {
4410Sstevel@tonic-gate if (ret = krb5_mk_priv(context, auth_context, &clear,
4420Sstevel@tonic-gate &cipher, &replay)) {
4430Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR;
4440Sstevel@tonic-gate (void) strlcpy(strresult,
4450Sstevel@tonic-gate "Failed encrypting reply",
4460Sstevel@tonic-gate sizeof (strresult));
4470Sstevel@tonic-gate }
4480Sstevel@tonic-gate }
4490Sstevel@tonic-gate }
4500Sstevel@tonic-gate
4510Sstevel@tonic-gate /*
4520Sstevel@tonic-gate * If no KRB-PRIV was constructed, then we need a KRB-ERROR.
4530Sstevel@tonic-gate * If this fails, just bail. There's nothing else we can do.
4540Sstevel@tonic-gate */
4550Sstevel@tonic-gate if (cipher.length == 0) {
4560Sstevel@tonic-gate /*
4570Sstevel@tonic-gate * Clear out ap_rep now, so that it won't be inserted
4580Sstevel@tonic-gate * in the reply
4590Sstevel@tonic-gate */
4600Sstevel@tonic-gate if (ap_rep.length) {
4610Sstevel@tonic-gate if (ap_rep.data != NULL)
4620Sstevel@tonic-gate krb5_xfree(ap_rep.data);
4630Sstevel@tonic-gate ap_rep.data = NULL;
4640Sstevel@tonic-gate ap_rep.length = 0;
4650Sstevel@tonic-gate }
4660Sstevel@tonic-gate
4670Sstevel@tonic-gate krberror.ctime = 0;
4680Sstevel@tonic-gate krberror.cusec = 0;
4690Sstevel@tonic-gate krberror.susec = 0;
4700Sstevel@tonic-gate if (ret = krb5_timeofday(context, &krberror.stime))
4710Sstevel@tonic-gate goto bailout;
4720Sstevel@tonic-gate
4730Sstevel@tonic-gate /*
4740Sstevel@tonic-gate * This is really icky. but it's what all the other callers
4750Sstevel@tonic-gate * to mk_error do.
4760Sstevel@tonic-gate */
4770Sstevel@tonic-gate krberror.error = ret;
4780Sstevel@tonic-gate krberror.error -= ERROR_TABLE_BASE_krb5;
4790Sstevel@tonic-gate if (krberror.error < 0 || krberror.error > 128)
4800Sstevel@tonic-gate krberror.error = KRB_ERR_GENERIC;
4810Sstevel@tonic-gate
4820Sstevel@tonic-gate krberror.client = NULL;
4830Sstevel@tonic-gate if (ret = krb5_build_principal(context, &krberror.server,
4840Sstevel@tonic-gate strlen(realm), realm,
4850Sstevel@tonic-gate "kadmin", "changepw", NULL)) {
4860Sstevel@tonic-gate goto bailout;
4870Sstevel@tonic-gate }
4880Sstevel@tonic-gate
4890Sstevel@tonic-gate krberror.text.length = 0;
4900Sstevel@tonic-gate krberror.e_data = clear;
4910Sstevel@tonic-gate
4920Sstevel@tonic-gate ret = krb5_mk_error(context, &krberror, &cipher);
4930Sstevel@tonic-gate
4940Sstevel@tonic-gate krb5_free_principal(context, krberror.server);
4950Sstevel@tonic-gate
4960Sstevel@tonic-gate if (ret)
4970Sstevel@tonic-gate goto bailout;
4980Sstevel@tonic-gate }
4990Sstevel@tonic-gate
5000Sstevel@tonic-gate /*
5010Sstevel@tonic-gate * Construct the reply
5020Sstevel@tonic-gate */
5030Sstevel@tonic-gate rep->length = 6 + ap_rep.length + cipher.length;
5040Sstevel@tonic-gate if ((rep->data = (char *)malloc(rep->length)) == NULL) {
5050Sstevel@tonic-gate ret = errno;
5060Sstevel@tonic-gate goto bailout;
5070Sstevel@tonic-gate }
5080Sstevel@tonic-gate ptr = rep->data;
5090Sstevel@tonic-gate
5100Sstevel@tonic-gate /*
5110Sstevel@tonic-gate * Length
5120Sstevel@tonic-gate */
5130Sstevel@tonic-gate *ptr++ = (rep->length>>8) & 0xff;
5140Sstevel@tonic-gate *ptr++ = rep->length & 0xff;
5150Sstevel@tonic-gate
5160Sstevel@tonic-gate /*
5170Sstevel@tonic-gate * Version == 0x0001 big-endian
5180Sstevel@tonic-gate */
5190Sstevel@tonic-gate *ptr++ = 0;
5200Sstevel@tonic-gate *ptr++ = 1;
5210Sstevel@tonic-gate
5220Sstevel@tonic-gate /*
5230Sstevel@tonic-gate * ap_rep length, big-endian
5240Sstevel@tonic-gate */
5250Sstevel@tonic-gate *ptr++ = (ap_rep.length>>8) & 0xff;
5260Sstevel@tonic-gate *ptr++ = ap_rep.length & 0xff;
5270Sstevel@tonic-gate
5280Sstevel@tonic-gate /*
5290Sstevel@tonic-gate * ap-rep data
5300Sstevel@tonic-gate */
5310Sstevel@tonic-gate if (ap_rep.length) {
5320Sstevel@tonic-gate (void) memcpy(ptr, ap_rep.data, ap_rep.length);
5330Sstevel@tonic-gate ptr += ap_rep.length;
5340Sstevel@tonic-gate }
5350Sstevel@tonic-gate
5360Sstevel@tonic-gate /*
5370Sstevel@tonic-gate * krb-priv or krb-error
5380Sstevel@tonic-gate */
5390Sstevel@tonic-gate (void) memcpy(ptr, cipher.data, cipher.length);
5400Sstevel@tonic-gate
5410Sstevel@tonic-gate bailout:
5420Sstevel@tonic-gate if (auth_context)
5430Sstevel@tonic-gate krb5_auth_con_free(context, auth_context);
5440Sstevel@tonic-gate if (changepw)
5450Sstevel@tonic-gate krb5_free_principal(context, changepw);
5460Sstevel@tonic-gate if (ap_rep.data != NULL)
5470Sstevel@tonic-gate krb5_xfree(ap_rep.data);
5480Sstevel@tonic-gate if (ticket)
5490Sstevel@tonic-gate krb5_free_ticket(context, ticket);
5500Sstevel@tonic-gate if (clear.data != NULL)
5510Sstevel@tonic-gate krb5_xfree(clear.data);
5520Sstevel@tonic-gate if (cipher.data != NULL)
5530Sstevel@tonic-gate krb5_xfree(cipher.data);
5540Sstevel@tonic-gate if (allocated_mem)
5550Sstevel@tonic-gate krb5_xfree(local_kaddr.contents);
5560Sstevel@tonic-gate
5570Sstevel@tonic-gate return (ret);
5580Sstevel@tonic-gate }
5590Sstevel@tonic-gate
5600Sstevel@tonic-gate
5610Sstevel@tonic-gate /*
5620Sstevel@tonic-gate * This routine is used to handle password-change requests received
5630Sstevel@tonic-gate * on kpasswd-port 464 from MIT/M$ clients.
5640Sstevel@tonic-gate */
5650Sstevel@tonic-gate void
handle_chpw(krb5_context context,int s1,void * serverhandle,kadm5_config_params * params)5660Sstevel@tonic-gate handle_chpw(krb5_context context, int s1,
5670Sstevel@tonic-gate void *serverhandle, kadm5_config_params *params)
5680Sstevel@tonic-gate {
5690Sstevel@tonic-gate krb5_error_code ret;
5700Sstevel@tonic-gate char req[MAXAPREQ];
5710Sstevel@tonic-gate int len;
5720Sstevel@tonic-gate struct sockaddr_in from;
5730Sstevel@tonic-gate int fromlen;
5740Sstevel@tonic-gate krb5_keytab kt;
5750Sstevel@tonic-gate krb5_data reqdata, repdata;
5760Sstevel@tonic-gate int s2 = -1;
5770Sstevel@tonic-gate
5780Sstevel@tonic-gate reqdata.length = 0;
5790Sstevel@tonic-gate reqdata.data = NULL;
5800Sstevel@tonic-gate repdata.length = 0;
5810Sstevel@tonic-gate repdata.data = NULL;
5820Sstevel@tonic-gate
5830Sstevel@tonic-gate fromlen = sizeof (from);
5840Sstevel@tonic-gate
5850Sstevel@tonic-gate if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from,
5860Sstevel@tonic-gate &fromlen)) < 0) {
5870Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive "
5880Sstevel@tonic-gate "request: %s"), error_message(errno));
5890Sstevel@tonic-gate return;
5900Sstevel@tonic-gate }
5910Sstevel@tonic-gate
592*11493SMark.Phalan@Sun.COM /*
593*11493SMark.Phalan@Sun.COM * Solaris Kerberos:
594*11493SMark.Phalan@Sun.COM * The only caller is kadmind, which is the master and therefore has the
595*11493SMark.Phalan@Sun.COM * correct keys in the KDB, rather than obtaining them via the
596*11493SMark.Phalan@Sun.COM * kadm5.keytab, by default.
597*11493SMark.Phalan@Sun.COM */
598*11493SMark.Phalan@Sun.COM if ((ret = krb5_kt_resolve(context, "KDB:", &kt))) {
5990Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open "
6000Sstevel@tonic-gate "admin keytab %s"), error_message(ret));
6010Sstevel@tonic-gate return;
6020Sstevel@tonic-gate }
6030Sstevel@tonic-gate
6040Sstevel@tonic-gate reqdata.length = len;
6050Sstevel@tonic-gate reqdata.data = req;
6060Sstevel@tonic-gate
6070Sstevel@tonic-gate /*
6080Sstevel@tonic-gate * This is really obscure. s1 is used for all communications. it
6090Sstevel@tonic-gate * is left unconnected in case the server is multihomed and routes
6100Sstevel@tonic-gate * are asymmetric. s2 is connected to resolve routes and get
6110Sstevel@tonic-gate * addresses. this is the *only* way to get proper addresses for
6120Sstevel@tonic-gate * multihomed hosts if routing is asymmetric.
6130Sstevel@tonic-gate *
6140Sstevel@tonic-gate * A related problem in the server, but not the client, is that
6150Sstevel@tonic-gate * many os's have no way to disconnect a connected udp socket, so
6160Sstevel@tonic-gate * the s2 socket needs to be closed and recreated for each
6170Sstevel@tonic-gate * request. The s1 socket must not be closed, or else queued
6180Sstevel@tonic-gate * requests will be lost.
6190Sstevel@tonic-gate *
6200Sstevel@tonic-gate * A "naive" client implementation (one socket, no connect,
6210Sstevel@tonic-gate * hostname resolution to get the local ip addr) will work and
6220Sstevel@tonic-gate * interoperate if the client is single-homed.
6230Sstevel@tonic-gate */
6240Sstevel@tonic-gate
6250Sstevel@tonic-gate if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
6260Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create "
6270Sstevel@tonic-gate "connecting socket: %s"), error_message(errno));
6280Sstevel@tonic-gate goto cleanup;
6290Sstevel@tonic-gate }
6300Sstevel@tonic-gate
6310Sstevel@tonic-gate if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) {
6320Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect "
6330Sstevel@tonic-gate "to client: %s"), error_message(errno));
6340Sstevel@tonic-gate if (s2 > 0)
6350Sstevel@tonic-gate (void) close(s2);
6360Sstevel@tonic-gate goto cleanup;
6370Sstevel@tonic-gate }
6380Sstevel@tonic-gate
6390Sstevel@tonic-gate if ((ret = process_chpw_request(context, serverhandle,
6400Sstevel@tonic-gate params->realm, s2, kt, &from,
6410Sstevel@tonic-gate &reqdata, &repdata))) {
6420Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing "
6430Sstevel@tonic-gate "request: %s"), error_message(ret));
6440Sstevel@tonic-gate }
6450Sstevel@tonic-gate
6460Sstevel@tonic-gate if (s2 > 0)
6470Sstevel@tonic-gate (void) close(s2);
6480Sstevel@tonic-gate
6490Sstevel@tonic-gate if (repdata.length == 0 || repdata.data == NULL) {
6500Sstevel@tonic-gate /*
6510Sstevel@tonic-gate * Just return. This means something really bad happened
6520Sstevel@tonic-gate */
6530Sstevel@tonic-gate goto cleanup;
6540Sstevel@tonic-gate }
6550Sstevel@tonic-gate
6560Sstevel@tonic-gate len = sendto(s1, repdata.data, repdata.length, 0,
6570Sstevel@tonic-gate (struct sockaddr *)&from, sizeof (from));
6580Sstevel@tonic-gate
6590Sstevel@tonic-gate if (len < repdata.length) {
6600Sstevel@tonic-gate krb5_xfree(repdata.data);
6610Sstevel@tonic-gate
6620Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:"
6630Sstevel@tonic-gate " %s"), error_message(errno));
6640Sstevel@tonic-gate goto cleanup;
6650Sstevel@tonic-gate }
6660Sstevel@tonic-gate
6670Sstevel@tonic-gate if (repdata.data != NULL)
6680Sstevel@tonic-gate krb5_xfree(repdata.data);
6690Sstevel@tonic-gate cleanup:
6700Sstevel@tonic-gate krb5_kt_close(context, kt);
6710Sstevel@tonic-gate }
672