10Sstevel@tonic-gate /* 21474Smp153739 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 30Sstevel@tonic-gate * Use is subject to license terms. 40Sstevel@tonic-gate */ 50Sstevel@tonic-gate 60Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 70Sstevel@tonic-gate 80Sstevel@tonic-gate /* 90Sstevel@tonic-gate * lib/krb5/kadm5/srv/chgpwd.c 100Sstevel@tonic-gate * 110Sstevel@tonic-gate * Copyright 1998 by the Massachusetts Institute of Technology. 120Sstevel@tonic-gate * All Rights Reserved. 130Sstevel@tonic-gate * 140Sstevel@tonic-gate * Export of this software from the United States of America may 150Sstevel@tonic-gate * require a specific license from the United States Government. 160Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 170Sstevel@tonic-gate * export to obtain such a license before exporting. 180Sstevel@tonic-gate * 190Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 200Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 210Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 220Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 230Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 240Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 250Sstevel@tonic-gate * to distribution of the software without specific, written prior 260Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 270Sstevel@tonic-gate * your software as modified software and not distribute it in such a 280Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 290Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 300Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 310Sstevel@tonic-gate * or implied warranty. 320Sstevel@tonic-gate * 330Sstevel@tonic-gate */ 340Sstevel@tonic-gate 350Sstevel@tonic-gate /* 360Sstevel@tonic-gate * chgpwd.c - Handles changepw requests issued from non-Solaris krb5 clients. 370Sstevel@tonic-gate */ 380Sstevel@tonic-gate 390Sstevel@tonic-gate #include <libintl.h> 400Sstevel@tonic-gate #include <locale.h> 410Sstevel@tonic-gate #include <kadm5/admin.h> 420Sstevel@tonic-gate #include <syslog.h> 430Sstevel@tonic-gate #include <krb5/adm_proto.h> 440Sstevel@tonic-gate 450Sstevel@tonic-gate #define MAXAPREQ 1500 460Sstevel@tonic-gate 470Sstevel@tonic-gate static krb5_error_code 480Sstevel@tonic-gate process_chpw_request(krb5_context context, void *server_handle, 490Sstevel@tonic-gate char *realm, int s, krb5_keytab keytab, 500Sstevel@tonic-gate struct sockaddr_in *sin, krb5_data *req, 510Sstevel@tonic-gate krb5_data *rep) 520Sstevel@tonic-gate { 530Sstevel@tonic-gate krb5_error_code ret; 540Sstevel@tonic-gate char *ptr; 550Sstevel@tonic-gate int plen, vno; 560Sstevel@tonic-gate krb5_address local_kaddr, remote_kaddr; 570Sstevel@tonic-gate int allocated_mem = 0; 580Sstevel@tonic-gate krb5_data ap_req, ap_rep; 590Sstevel@tonic-gate krb5_auth_context auth_context; 600Sstevel@tonic-gate krb5_principal changepw; 610Sstevel@tonic-gate krb5_ticket *ticket; 620Sstevel@tonic-gate krb5_data cipher, clear; 630Sstevel@tonic-gate struct sockaddr local_addr, remote_addr; 640Sstevel@tonic-gate int addrlen; 650Sstevel@tonic-gate krb5_replay_data replay; 660Sstevel@tonic-gate krb5_error krberror; 670Sstevel@tonic-gate int numresult; 680Sstevel@tonic-gate char strresult[1024]; 69*2941Ssemery char *clientstr; 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 344*2941Ssemery ret = krb5_unparse_name(context, ticket->enc_part2->client, &clientstr); 345*2941Ssemery if (ret) { 346*2941Ssemery numresult = KRB5_KPASSWD_HARDERROR; 347*2941Ssemery (void) strcpy(strresult, "Failed decrypting request"); 348*2941Ssemery goto chpwfail; 349*2941Ssemery } 350*2941Ssemery 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)); 359*2941Ssemery 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)); 369*2941Ssemery 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 382*2941Ssemery krb5_klog_syslog(LOG_NOTICE, "chpw request from %s for %s: %s", 383*2941Ssemery inet_ntoa(((struct sockaddr_in *)&remote_addr)->sin_addr), 384*2941Ssemery clientstr, ret ? error_message(ret) : "success"); 385*2941Ssemery krb5_free_unparsed_name(context, clientstr); 386*2941Ssemery 3870Sstevel@tonic-gate if (ret) { 3880Sstevel@tonic-gate if ((ret != KADM5_PASS_Q_TOOSHORT) && 3890Sstevel@tonic-gate (ret != KADM5_PASS_REUSE) && 3900Sstevel@tonic-gate (ret != KADM5_PASS_Q_CLASS) && 3910Sstevel@tonic-gate (ret != KADM5_PASS_Q_DICT) && 3920Sstevel@tonic-gate (ret != KADM5_PASS_TOOSOON)) 3930Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3940Sstevel@tonic-gate else 3950Sstevel@tonic-gate numresult = KRB5_KPASSWD_SOFTERROR; 3960Sstevel@tonic-gate /* 3970Sstevel@tonic-gate * strresult set by kadb5_chpass_principal_util() 3980Sstevel@tonic-gate */ 3990Sstevel@tonic-gate goto chpwfail; 4000Sstevel@tonic-gate } 4010Sstevel@tonic-gate 4020Sstevel@tonic-gate /* 4030Sstevel@tonic-gate * Success! 4040Sstevel@tonic-gate */ 4050Sstevel@tonic-gate numresult = KRB5_KPASSWD_SUCCESS; 4060Sstevel@tonic-gate (void) strlcpy(strresult, "", sizeof (strresult)); 4070Sstevel@tonic-gate 4080Sstevel@tonic-gate chpwfail: 4090Sstevel@tonic-gate 4100Sstevel@tonic-gate clear.length = 2 + strlen(strresult); 4110Sstevel@tonic-gate if (clear.data != NULL) { 4120Sstevel@tonic-gate krb5_xfree(clear.data); 4130Sstevel@tonic-gate clear.data = NULL; 4140Sstevel@tonic-gate } 4150Sstevel@tonic-gate if ((clear.data = (char *)malloc(clear.length)) == NULL) { 4160Sstevel@tonic-gate ret = errno; 4170Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4180Sstevel@tonic-gate (void) strlcpy(strresult, "Malloc failed for clear.data", 4190Sstevel@tonic-gate sizeof (strresult)); 4200Sstevel@tonic-gate } 4210Sstevel@tonic-gate 422*2941Ssemery ptr = clear.data; 423*2941Ssemery 424*2941Ssemery *ptr++ = (numresult>>8) & 0xff; 425*2941Ssemery *ptr++ = numresult & 0xff; 426*2941Ssemery 427*2941Ssemery (void) memcpy(ptr, strresult, strlen(strresult)); 428*2941Ssemery 4290Sstevel@tonic-gate cipher.length = 0; 4300Sstevel@tonic-gate 4310Sstevel@tonic-gate if (ap_rep.length) { 4320Sstevel@tonic-gate if (ret = krb5_auth_con_setaddrs(context, auth_context, 4330Sstevel@tonic-gate &local_kaddr, NULL)) { 4340Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4350Sstevel@tonic-gate (void) strlcpy(strresult, 4360Sstevel@tonic-gate "Failed storing client and server internet addresses", 4370Sstevel@tonic-gate sizeof (strresult)); 4380Sstevel@tonic-gate } else { 4390Sstevel@tonic-gate if (ret = krb5_mk_priv(context, auth_context, &clear, 4400Sstevel@tonic-gate &cipher, &replay)) { 4410Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4420Sstevel@tonic-gate (void) strlcpy(strresult, 4430Sstevel@tonic-gate "Failed encrypting reply", 4440Sstevel@tonic-gate sizeof (strresult)); 4450Sstevel@tonic-gate } 4460Sstevel@tonic-gate } 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate /* 4500Sstevel@tonic-gate * If no KRB-PRIV was constructed, then we need a KRB-ERROR. 4510Sstevel@tonic-gate * If this fails, just bail. There's nothing else we can do. 4520Sstevel@tonic-gate */ 4530Sstevel@tonic-gate if (cipher.length == 0) { 4540Sstevel@tonic-gate /* 4550Sstevel@tonic-gate * Clear out ap_rep now, so that it won't be inserted 4560Sstevel@tonic-gate * in the reply 4570Sstevel@tonic-gate */ 4580Sstevel@tonic-gate if (ap_rep.length) { 4590Sstevel@tonic-gate if (ap_rep.data != NULL) 4600Sstevel@tonic-gate krb5_xfree(ap_rep.data); 4610Sstevel@tonic-gate ap_rep.data = NULL; 4620Sstevel@tonic-gate ap_rep.length = 0; 4630Sstevel@tonic-gate } 4640Sstevel@tonic-gate 4650Sstevel@tonic-gate krberror.ctime = 0; 4660Sstevel@tonic-gate krberror.cusec = 0; 4670Sstevel@tonic-gate krberror.susec = 0; 4680Sstevel@tonic-gate if (ret = krb5_timeofday(context, &krberror.stime)) 4690Sstevel@tonic-gate goto bailout; 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate /* 4720Sstevel@tonic-gate * This is really icky. but it's what all the other callers 4730Sstevel@tonic-gate * to mk_error do. 4740Sstevel@tonic-gate */ 4750Sstevel@tonic-gate krberror.error = ret; 4760Sstevel@tonic-gate krberror.error -= ERROR_TABLE_BASE_krb5; 4770Sstevel@tonic-gate if (krberror.error < 0 || krberror.error > 128) 4780Sstevel@tonic-gate krberror.error = KRB_ERR_GENERIC; 4790Sstevel@tonic-gate 4800Sstevel@tonic-gate krberror.client = NULL; 4810Sstevel@tonic-gate if (ret = krb5_build_principal(context, &krberror.server, 4820Sstevel@tonic-gate strlen(realm), realm, 4830Sstevel@tonic-gate "kadmin", "changepw", NULL)) { 4840Sstevel@tonic-gate goto bailout; 4850Sstevel@tonic-gate } 4860Sstevel@tonic-gate 4870Sstevel@tonic-gate krberror.text.length = 0; 4880Sstevel@tonic-gate krberror.e_data = clear; 4890Sstevel@tonic-gate 4900Sstevel@tonic-gate ret = krb5_mk_error(context, &krberror, &cipher); 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate krb5_free_principal(context, krberror.server); 4930Sstevel@tonic-gate 4940Sstevel@tonic-gate if (ret) 4950Sstevel@tonic-gate goto bailout; 4960Sstevel@tonic-gate } 4970Sstevel@tonic-gate 4980Sstevel@tonic-gate /* 4990Sstevel@tonic-gate * Construct the reply 5000Sstevel@tonic-gate */ 5010Sstevel@tonic-gate rep->length = 6 + ap_rep.length + cipher.length; 5020Sstevel@tonic-gate if ((rep->data = (char *)malloc(rep->length)) == NULL) { 5030Sstevel@tonic-gate ret = errno; 5040Sstevel@tonic-gate goto bailout; 5050Sstevel@tonic-gate } 5060Sstevel@tonic-gate ptr = rep->data; 5070Sstevel@tonic-gate 5080Sstevel@tonic-gate /* 5090Sstevel@tonic-gate * Length 5100Sstevel@tonic-gate */ 5110Sstevel@tonic-gate *ptr++ = (rep->length>>8) & 0xff; 5120Sstevel@tonic-gate *ptr++ = rep->length & 0xff; 5130Sstevel@tonic-gate 5140Sstevel@tonic-gate /* 5150Sstevel@tonic-gate * Version == 0x0001 big-endian 5160Sstevel@tonic-gate */ 5170Sstevel@tonic-gate *ptr++ = 0; 5180Sstevel@tonic-gate *ptr++ = 1; 5190Sstevel@tonic-gate 5200Sstevel@tonic-gate /* 5210Sstevel@tonic-gate * ap_rep length, big-endian 5220Sstevel@tonic-gate */ 5230Sstevel@tonic-gate *ptr++ = (ap_rep.length>>8) & 0xff; 5240Sstevel@tonic-gate *ptr++ = ap_rep.length & 0xff; 5250Sstevel@tonic-gate 5260Sstevel@tonic-gate /* 5270Sstevel@tonic-gate * ap-rep data 5280Sstevel@tonic-gate */ 5290Sstevel@tonic-gate if (ap_rep.length) { 5300Sstevel@tonic-gate (void) memcpy(ptr, ap_rep.data, ap_rep.length); 5310Sstevel@tonic-gate ptr += ap_rep.length; 5320Sstevel@tonic-gate } 5330Sstevel@tonic-gate 5340Sstevel@tonic-gate /* 5350Sstevel@tonic-gate * krb-priv or krb-error 5360Sstevel@tonic-gate */ 5370Sstevel@tonic-gate (void) memcpy(ptr, cipher.data, cipher.length); 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate bailout: 5400Sstevel@tonic-gate if (auth_context) 5410Sstevel@tonic-gate krb5_auth_con_free(context, auth_context); 5420Sstevel@tonic-gate if (changepw) 5430Sstevel@tonic-gate krb5_free_principal(context, changepw); 5440Sstevel@tonic-gate if (ap_rep.data != NULL) 5450Sstevel@tonic-gate krb5_xfree(ap_rep.data); 5460Sstevel@tonic-gate if (ticket) 5470Sstevel@tonic-gate krb5_free_ticket(context, ticket); 5480Sstevel@tonic-gate if (clear.data != NULL) 5490Sstevel@tonic-gate krb5_xfree(clear.data); 5500Sstevel@tonic-gate if (cipher.data != NULL) 5510Sstevel@tonic-gate krb5_xfree(cipher.data); 5520Sstevel@tonic-gate if (allocated_mem) 5530Sstevel@tonic-gate krb5_xfree(local_kaddr.contents); 5540Sstevel@tonic-gate 5550Sstevel@tonic-gate return (ret); 5560Sstevel@tonic-gate } 5570Sstevel@tonic-gate 5580Sstevel@tonic-gate 5590Sstevel@tonic-gate /* 5600Sstevel@tonic-gate * This routine is used to handle password-change requests received 5610Sstevel@tonic-gate * on kpasswd-port 464 from MIT/M$ clients. 5620Sstevel@tonic-gate */ 5630Sstevel@tonic-gate void 5640Sstevel@tonic-gate handle_chpw(krb5_context context, int s1, 5650Sstevel@tonic-gate void *serverhandle, kadm5_config_params *params) 5660Sstevel@tonic-gate { 5670Sstevel@tonic-gate krb5_error_code ret; 5680Sstevel@tonic-gate char req[MAXAPREQ]; 5690Sstevel@tonic-gate int len; 5700Sstevel@tonic-gate struct sockaddr_in from; 5710Sstevel@tonic-gate int fromlen; 5720Sstevel@tonic-gate krb5_keytab kt; 5730Sstevel@tonic-gate krb5_data reqdata, repdata; 5740Sstevel@tonic-gate int s2 = -1; 5750Sstevel@tonic-gate 5760Sstevel@tonic-gate reqdata.length = 0; 5770Sstevel@tonic-gate reqdata.data = NULL; 5780Sstevel@tonic-gate repdata.length = 0; 5790Sstevel@tonic-gate repdata.data = NULL; 5800Sstevel@tonic-gate 5810Sstevel@tonic-gate fromlen = sizeof (from); 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from, 5840Sstevel@tonic-gate &fromlen)) < 0) { 5850Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive " 5860Sstevel@tonic-gate "request: %s"), error_message(errno)); 5870Sstevel@tonic-gate return; 5880Sstevel@tonic-gate } 5890Sstevel@tonic-gate 5900Sstevel@tonic-gate if ((ret = krb5_kt_resolve(context, params->admin_keytab, &kt))) { 5910Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open " 5920Sstevel@tonic-gate "admin keytab %s"), error_message(ret)); 5930Sstevel@tonic-gate return; 5940Sstevel@tonic-gate } 5950Sstevel@tonic-gate 5960Sstevel@tonic-gate reqdata.length = len; 5970Sstevel@tonic-gate reqdata.data = req; 5980Sstevel@tonic-gate 5990Sstevel@tonic-gate /* 6000Sstevel@tonic-gate * This is really obscure. s1 is used for all communications. it 6010Sstevel@tonic-gate * is left unconnected in case the server is multihomed and routes 6020Sstevel@tonic-gate * are asymmetric. s2 is connected to resolve routes and get 6030Sstevel@tonic-gate * addresses. this is the *only* way to get proper addresses for 6040Sstevel@tonic-gate * multihomed hosts if routing is asymmetric. 6050Sstevel@tonic-gate * 6060Sstevel@tonic-gate * A related problem in the server, but not the client, is that 6070Sstevel@tonic-gate * many os's have no way to disconnect a connected udp socket, so 6080Sstevel@tonic-gate * the s2 socket needs to be closed and recreated for each 6090Sstevel@tonic-gate * request. The s1 socket must not be closed, or else queued 6100Sstevel@tonic-gate * requests will be lost. 6110Sstevel@tonic-gate * 6120Sstevel@tonic-gate * A "naive" client implementation (one socket, no connect, 6130Sstevel@tonic-gate * hostname resolution to get the local ip addr) will work and 6140Sstevel@tonic-gate * interoperate if the client is single-homed. 6150Sstevel@tonic-gate */ 6160Sstevel@tonic-gate 6170Sstevel@tonic-gate if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 6180Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create " 6190Sstevel@tonic-gate "connecting socket: %s"), error_message(errno)); 6200Sstevel@tonic-gate goto cleanup; 6210Sstevel@tonic-gate } 6220Sstevel@tonic-gate 6230Sstevel@tonic-gate if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) { 6240Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect " 6250Sstevel@tonic-gate "to client: %s"), error_message(errno)); 6260Sstevel@tonic-gate if (s2 > 0) 6270Sstevel@tonic-gate (void) close(s2); 6280Sstevel@tonic-gate goto cleanup; 6290Sstevel@tonic-gate } 6300Sstevel@tonic-gate 6310Sstevel@tonic-gate if ((ret = process_chpw_request(context, serverhandle, 6320Sstevel@tonic-gate params->realm, s2, kt, &from, 6330Sstevel@tonic-gate &reqdata, &repdata))) { 6340Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing " 6350Sstevel@tonic-gate "request: %s"), error_message(ret)); 6360Sstevel@tonic-gate } 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate if (s2 > 0) 6390Sstevel@tonic-gate (void) close(s2); 6400Sstevel@tonic-gate 6410Sstevel@tonic-gate if (repdata.length == 0 || repdata.data == NULL) { 6420Sstevel@tonic-gate /* 6430Sstevel@tonic-gate * Just return. This means something really bad happened 6440Sstevel@tonic-gate */ 6450Sstevel@tonic-gate goto cleanup; 6460Sstevel@tonic-gate } 6470Sstevel@tonic-gate 6480Sstevel@tonic-gate len = sendto(s1, repdata.data, repdata.length, 0, 6490Sstevel@tonic-gate (struct sockaddr *)&from, sizeof (from)); 6500Sstevel@tonic-gate 6510Sstevel@tonic-gate if (len < repdata.length) { 6520Sstevel@tonic-gate krb5_xfree(repdata.data); 6530Sstevel@tonic-gate 6540Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:" 6550Sstevel@tonic-gate " %s"), error_message(errno)); 6560Sstevel@tonic-gate goto cleanup; 6570Sstevel@tonic-gate } 6580Sstevel@tonic-gate 6590Sstevel@tonic-gate if (repdata.data != NULL) 6600Sstevel@tonic-gate krb5_xfree(repdata.data); 6610Sstevel@tonic-gate cleanup: 6620Sstevel@tonic-gate krb5_kt_close(context, kt); 6630Sstevel@tonic-gate } 664