10Sstevel@tonic-gate /* 2*1474Smp153739 * 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]; 690Sstevel@tonic-gate 700Sstevel@tonic-gate ret = 0; 710Sstevel@tonic-gate rep->length = 0; 720Sstevel@tonic-gate 730Sstevel@tonic-gate auth_context = NULL; 740Sstevel@tonic-gate changepw = NULL; 750Sstevel@tonic-gate ap_rep.length = 0; 760Sstevel@tonic-gate ap_rep.data = NULL; 770Sstevel@tonic-gate ticket = NULL; 780Sstevel@tonic-gate clear.length = 0; 790Sstevel@tonic-gate clear.data = NULL; 800Sstevel@tonic-gate cipher.length = 0; 810Sstevel@tonic-gate cipher.data = NULL; 820Sstevel@tonic-gate 830Sstevel@tonic-gate if (req->length < 4) { 840Sstevel@tonic-gate /* 850Sstevel@tonic-gate * either this, or the server is printing bad messages, 860Sstevel@tonic-gate * or the caller passed in garbage 870Sstevel@tonic-gate */ 880Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED; 890Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED; 900Sstevel@tonic-gate (void) strlcpy(strresult, "Request was truncated", 910Sstevel@tonic-gate sizeof (strresult)); 920Sstevel@tonic-gate goto chpwfail; 930Sstevel@tonic-gate } 940Sstevel@tonic-gate 950Sstevel@tonic-gate ptr = req->data; 960Sstevel@tonic-gate 970Sstevel@tonic-gate /* 980Sstevel@tonic-gate * Verify length 990Sstevel@tonic-gate */ 1000Sstevel@tonic-gate plen = (*ptr++ & 0xff); 1010Sstevel@tonic-gate plen = (plen<<8) | (*ptr++ & 0xff); 1020Sstevel@tonic-gate 1030Sstevel@tonic-gate if (plen != req->length) 1040Sstevel@tonic-gate return (KRB5KRB_AP_ERR_MODIFIED); 1050Sstevel@tonic-gate 1060Sstevel@tonic-gate /* 1070Sstevel@tonic-gate * Verify version number 1080Sstevel@tonic-gate */ 1090Sstevel@tonic-gate vno = (*ptr++ & 0xff); 1100Sstevel@tonic-gate vno = (vno<<8) | (*ptr++ & 0xff); 1110Sstevel@tonic-gate 1120Sstevel@tonic-gate if (vno != 1) { 1130Sstevel@tonic-gate ret = KRB5KDC_ERR_BAD_PVNO; 1140Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED; 1150Sstevel@tonic-gate (void) snprintf(strresult, sizeof (strresult), 1160Sstevel@tonic-gate "Request contained unknown protocol version number %d", 1170Sstevel@tonic-gate vno); 1180Sstevel@tonic-gate goto chpwfail; 1190Sstevel@tonic-gate } 1200Sstevel@tonic-gate 1210Sstevel@tonic-gate /* 1220Sstevel@tonic-gate * Read, check ap-req length 1230Sstevel@tonic-gate */ 1240Sstevel@tonic-gate ap_req.length = (*ptr++ & 0xff); 1250Sstevel@tonic-gate ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); 1260Sstevel@tonic-gate 1270Sstevel@tonic-gate if (ptr + ap_req.length >= req->data + req->length) { 1280Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED; 1290Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED; 1300Sstevel@tonic-gate (void) strlcpy(strresult, "Request was truncated in AP-REQ", 1310Sstevel@tonic-gate sizeof (strresult)); 1320Sstevel@tonic-gate goto chpwfail; 1330Sstevel@tonic-gate } 1340Sstevel@tonic-gate 1350Sstevel@tonic-gate /* 1360Sstevel@tonic-gate * Verify ap_req 1370Sstevel@tonic-gate */ 1380Sstevel@tonic-gate ap_req.data = ptr; 1390Sstevel@tonic-gate ptr += ap_req.length; 1400Sstevel@tonic-gate 1410Sstevel@tonic-gate if (ret = krb5_auth_con_init(context, &auth_context)) { 142*1474Smp153739 krb5_klog_syslog(LOG_ERR, 143*1474Smp153739 gettext("Change password request failed. " 144*1474Smp153739 "Failed initializing auth context: %s"), 145*1474Smp153739 error_message(ret)); 1460Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 1470Sstevel@tonic-gate (void) strlcpy(strresult, "Failed initializing auth context", 1480Sstevel@tonic-gate sizeof (strresult)); 1490Sstevel@tonic-gate goto chpwfail; 1500Sstevel@tonic-gate } 1510Sstevel@tonic-gate 1520Sstevel@tonic-gate if (ret = krb5_auth_con_setflags(context, auth_context, 1530Sstevel@tonic-gate KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { 154*1474Smp153739 krb5_klog_syslog(LOG_ERR, 155*1474Smp153739 gettext("Change password request failed. " 156*1474Smp153739 "Failed setting auth " 157*1474Smp153739 "context flags: %s"), 158*1474Smp153739 error_message(ret)); 1590Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 1600Sstevel@tonic-gate (void) strlcpy(strresult, "Failed initializing auth context", 1610Sstevel@tonic-gate sizeof (strresult)); 1620Sstevel@tonic-gate goto chpwfail; 1630Sstevel@tonic-gate } 1640Sstevel@tonic-gate 1650Sstevel@tonic-gate if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm, 1660Sstevel@tonic-gate "kadmin", "changepw", NULL)) { 167*1474Smp153739 krb5_klog_syslog(LOG_ERR, 168*1474Smp153739 gettext("Change password request failed " 169*1474Smp153739 "Failed to build kadmin/changepw " 170*1474Smp153739 "principal: %s"), 171*1474Smp153739 error_message(ret)); 1720Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 1730Sstevel@tonic-gate (void) strlcpy(strresult, 1740Sstevel@tonic-gate "Failed building kadmin/changepw principal", 1750Sstevel@tonic-gate sizeof (strresult)); 1760Sstevel@tonic-gate goto chpwfail; 1770Sstevel@tonic-gate } 1780Sstevel@tonic-gate 1790Sstevel@tonic-gate ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, 1800Sstevel@tonic-gate NULL, &ticket); 1810Sstevel@tonic-gate 1820Sstevel@tonic-gate if (ret) { 183*1474Smp153739 char kt_name[MAX_KEYTAB_NAME_LEN]; 184*1474Smp153739 if (krb5_kt_get_name(context, keytab, 185*1474Smp153739 kt_name, sizeof (kt_name))) 186*1474Smp153739 strncpy(kt_name, "default keytab", sizeof (kt_name)); 187*1474Smp153739 188*1474Smp153739 switch (ret) { 189*1474Smp153739 case KRB5_KT_NOTFOUND: 190*1474Smp153739 krb5_klog_syslog(LOG_ERR, 191*1474Smp153739 gettext("Change password request failed because " 192*1474Smp153739 "keytab entry \"kadmin/changepw\" " 193*1474Smp153739 "is missing from \"%s\""), 194*1474Smp153739 kt_name); 195*1474Smp153739 break; 196*1474Smp153739 case ENOENT: 197*1474Smp153739 krb5_klog_syslog(LOG_ERR, 198*1474Smp153739 gettext("Change password request failed because " 199*1474Smp153739 "keytab file \"%s\" does not exist"), 200*1474Smp153739 kt_name); 201*1474Smp153739 break; 202*1474Smp153739 default: 203*1474Smp153739 krb5_klog_syslog(LOG_ERR, 204*1474Smp153739 gettext("Change password request failed. " 205*1474Smp153739 "Failed to parse Kerberos AP_REQ message: %s"), 206*1474Smp153739 error_message(ret)); 207*1474Smp153739 } 208*1474Smp153739 2090Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR; 2100Sstevel@tonic-gate (void) strlcpy(strresult, "Failed reading application request", 2110Sstevel@tonic-gate sizeof (strresult)); 2120Sstevel@tonic-gate goto chpwfail; 2130Sstevel@tonic-gate } 2140Sstevel@tonic-gate 2150Sstevel@tonic-gate /* 2160Sstevel@tonic-gate * Set up address info 2170Sstevel@tonic-gate */ 2180Sstevel@tonic-gate addrlen = sizeof (local_addr); 2190Sstevel@tonic-gate 2200Sstevel@tonic-gate if (getsockname(s, &local_addr, &addrlen) < 0) { 2210Sstevel@tonic-gate ret = errno; 2220Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 2230Sstevel@tonic-gate (void) strlcpy(strresult, 2240Sstevel@tonic-gate "Failed getting server internet address", 2250Sstevel@tonic-gate sizeof (strresult)); 2260Sstevel@tonic-gate goto chpwfail; 2270Sstevel@tonic-gate } 2280Sstevel@tonic-gate 2290Sstevel@tonic-gate /* 2300Sstevel@tonic-gate * Some brain-dead OS's don't return useful information from 2310Sstevel@tonic-gate * the getsockname call. Namely, windows and solaris. 2320Sstevel@tonic-gate */ 2330Sstevel@tonic-gate if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) { 2340Sstevel@tonic-gate local_kaddr.addrtype = ADDRTYPE_INET; 2350Sstevel@tonic-gate local_kaddr.length = sizeof (((struct sockaddr_in *) 2360Sstevel@tonic-gate &local_addr)->sin_addr); 2370Sstevel@tonic-gate /* CSTYLED */ 2380Sstevel@tonic-gate local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr); 2390Sstevel@tonic-gate } else { 2400Sstevel@tonic-gate krb5_address **addrs; 2410Sstevel@tonic-gate 2420Sstevel@tonic-gate krb5_os_localaddr(context, &addrs); 2430Sstevel@tonic-gate 2440Sstevel@tonic-gate local_kaddr.magic = addrs[0]->magic; 2450Sstevel@tonic-gate local_kaddr.addrtype = addrs[0]->addrtype; 2460Sstevel@tonic-gate local_kaddr.length = addrs[0]->length; 2470Sstevel@tonic-gate if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) { 2480Sstevel@tonic-gate ret = errno; 2490Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 2500Sstevel@tonic-gate (void) strlcpy(strresult, 2510Sstevel@tonic-gate "Malloc failed for local_kaddr", 2520Sstevel@tonic-gate sizeof (strresult)); 2530Sstevel@tonic-gate goto chpwfail; 2540Sstevel@tonic-gate } 2550Sstevel@tonic-gate 2560Sstevel@tonic-gate (void) memcpy(local_kaddr.contents, addrs[0]->contents, 2570Sstevel@tonic-gate addrs[0]->length); 2580Sstevel@tonic-gate allocated_mem++; 2590Sstevel@tonic-gate 2600Sstevel@tonic-gate krb5_free_addresses(context, addrs); 2610Sstevel@tonic-gate } 2620Sstevel@tonic-gate 2630Sstevel@tonic-gate addrlen = sizeof (remote_addr); 2640Sstevel@tonic-gate 2650Sstevel@tonic-gate if (getpeername(s, &remote_addr, &addrlen) < 0) { 2660Sstevel@tonic-gate ret = errno; 2670Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 2680Sstevel@tonic-gate (void) strlcpy(strresult, 2690Sstevel@tonic-gate "Failed getting client internet address", 2700Sstevel@tonic-gate sizeof (strresult)); 2710Sstevel@tonic-gate goto chpwfail; 2720Sstevel@tonic-gate } 2730Sstevel@tonic-gate 2740Sstevel@tonic-gate remote_kaddr.addrtype = ADDRTYPE_INET; 2750Sstevel@tonic-gate remote_kaddr.length = sizeof (((struct sockaddr_in *) 2760Sstevel@tonic-gate &remote_addr)->sin_addr); 2770Sstevel@tonic-gate /* CSTYLED */ 2780Sstevel@tonic-gate remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr); 2790Sstevel@tonic-gate remote_kaddr.addrtype = ADDRTYPE_INET; 2800Sstevel@tonic-gate remote_kaddr.length = sizeof (sin->sin_addr); 2810Sstevel@tonic-gate remote_kaddr.contents = (krb5_octet *) &sin->sin_addr; 2820Sstevel@tonic-gate 2830Sstevel@tonic-gate /* 2840Sstevel@tonic-gate * mk_priv requires that the local address be set. 2850Sstevel@tonic-gate * getsockname is used for this. rd_priv requires that the 2860Sstevel@tonic-gate * remote address be set. recvfrom is used for this. If 2870Sstevel@tonic-gate * rd_priv is given a local address, and the message has the 2880Sstevel@tonic-gate * recipient addr in it, this will be checked. However, there 2890Sstevel@tonic-gate * is simply no way to know ahead of time what address the 2900Sstevel@tonic-gate * message will be delivered *to*. Therefore, it is important 2910Sstevel@tonic-gate * that either no recipient address is in the messages when 2920Sstevel@tonic-gate * mk_priv is called, or that no local address is passed to 2930Sstevel@tonic-gate * rd_priv. Both is a better idea, and I have done that. In 2940Sstevel@tonic-gate * summary, when mk_priv is called, *only* a local address is 2950Sstevel@tonic-gate * specified. when rd_priv is called, *only* a remote address 2960Sstevel@tonic-gate * is specified. Are we having fun yet? 2970Sstevel@tonic-gate */ 2980Sstevel@tonic-gate if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL, 2990Sstevel@tonic-gate &remote_kaddr)) { 3000Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3010Sstevel@tonic-gate (void) strlcpy(strresult, 3020Sstevel@tonic-gate "Failed storing client internet address", 3030Sstevel@tonic-gate sizeof (strresult)); 3040Sstevel@tonic-gate goto chpwfail; 3050Sstevel@tonic-gate } 3060Sstevel@tonic-gate 3070Sstevel@tonic-gate /* 3080Sstevel@tonic-gate * Verify that this is an AS_REQ ticket 3090Sstevel@tonic-gate */ 3100Sstevel@tonic-gate if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) { 3110Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR; 3120Sstevel@tonic-gate (void) strlcpy(strresult, 3130Sstevel@tonic-gate "Ticket must be derived from a password", 3140Sstevel@tonic-gate sizeof (strresult)); 3150Sstevel@tonic-gate goto chpwfail; 3160Sstevel@tonic-gate } 3170Sstevel@tonic-gate 3180Sstevel@tonic-gate /* 3190Sstevel@tonic-gate * Construct the ap-rep 3200Sstevel@tonic-gate */ 3210Sstevel@tonic-gate if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) { 3220Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR; 3230Sstevel@tonic-gate (void) strlcpy(strresult, 3240Sstevel@tonic-gate "Failed replying to application request", 3250Sstevel@tonic-gate sizeof (strresult)); 3260Sstevel@tonic-gate goto chpwfail; 3270Sstevel@tonic-gate } 3280Sstevel@tonic-gate 3290Sstevel@tonic-gate /* 3300Sstevel@tonic-gate * Decrypt the new password 3310Sstevel@tonic-gate */ 3320Sstevel@tonic-gate cipher.length = (req->data + req->length) - ptr; 3330Sstevel@tonic-gate cipher.data = ptr; 3340Sstevel@tonic-gate 3350Sstevel@tonic-gate if (ret = krb5_rd_priv(context, auth_context, &cipher, 3360Sstevel@tonic-gate &clear, &replay)) { 3370Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3380Sstevel@tonic-gate (void) strlcpy(strresult, "Failed decrypting request", 3390Sstevel@tonic-gate sizeof (strresult)); 3400Sstevel@tonic-gate goto chpwfail; 3410Sstevel@tonic-gate } 3420Sstevel@tonic-gate 3430Sstevel@tonic-gate /* 3440Sstevel@tonic-gate * Change the password 3450Sstevel@tonic-gate */ 3460Sstevel@tonic-gate if ((ptr = (char *)malloc(clear.length + 1)) == NULL) { 3470Sstevel@tonic-gate ret = errno; 3480Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3490Sstevel@tonic-gate (void) strlcpy(strresult, "Malloc failed for ptr", 3500Sstevel@tonic-gate sizeof (strresult)); 3510Sstevel@tonic-gate goto chpwfail; 3520Sstevel@tonic-gate } 3530Sstevel@tonic-gate (void) memcpy(ptr, clear.data, clear.length); 3540Sstevel@tonic-gate ptr[clear.length] = '\0'; 3550Sstevel@tonic-gate 3560Sstevel@tonic-gate ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle, 3570Sstevel@tonic-gate ticket->enc_part2->client, 3580Sstevel@tonic-gate ptr, NULL, strresult, 3590Sstevel@tonic-gate sizeof (strresult)); 3600Sstevel@tonic-gate /* 3610Sstevel@tonic-gate * Zap the password 3620Sstevel@tonic-gate */ 3630Sstevel@tonic-gate (void) memset(clear.data, 0, clear.length); 3640Sstevel@tonic-gate (void) memset(ptr, 0, clear.length); 3650Sstevel@tonic-gate if (clear.data != NULL) { 3660Sstevel@tonic-gate krb5_xfree(clear.data); 3670Sstevel@tonic-gate clear.data = NULL; 3680Sstevel@tonic-gate } 3690Sstevel@tonic-gate free(ptr); 3700Sstevel@tonic-gate clear.length = 0; 3710Sstevel@tonic-gate 3720Sstevel@tonic-gate if (ret) { 3730Sstevel@tonic-gate if ((ret != KADM5_PASS_Q_TOOSHORT) && 3740Sstevel@tonic-gate (ret != KADM5_PASS_REUSE) && 3750Sstevel@tonic-gate (ret != KADM5_PASS_Q_CLASS) && 3760Sstevel@tonic-gate (ret != KADM5_PASS_Q_DICT) && 3770Sstevel@tonic-gate (ret != KADM5_PASS_TOOSOON)) 3780Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3790Sstevel@tonic-gate else 3800Sstevel@tonic-gate numresult = KRB5_KPASSWD_SOFTERROR; 3810Sstevel@tonic-gate /* 3820Sstevel@tonic-gate * strresult set by kadb5_chpass_principal_util() 3830Sstevel@tonic-gate */ 3840Sstevel@tonic-gate goto chpwfail; 3850Sstevel@tonic-gate } 3860Sstevel@tonic-gate 3870Sstevel@tonic-gate /* 3880Sstevel@tonic-gate * Success! 3890Sstevel@tonic-gate */ 3900Sstevel@tonic-gate numresult = KRB5_KPASSWD_SUCCESS; 3910Sstevel@tonic-gate (void) strlcpy(strresult, "", sizeof (strresult)); 3920Sstevel@tonic-gate 3930Sstevel@tonic-gate chpwfail: 3940Sstevel@tonic-gate 3950Sstevel@tonic-gate clear.length = 2 + strlen(strresult); 3960Sstevel@tonic-gate if (clear.data != NULL) { 3970Sstevel@tonic-gate krb5_xfree(clear.data); 3980Sstevel@tonic-gate clear.data = NULL; 3990Sstevel@tonic-gate } 4000Sstevel@tonic-gate if ((clear.data = (char *)malloc(clear.length)) == NULL) { 4010Sstevel@tonic-gate ret = errno; 4020Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4030Sstevel@tonic-gate (void) strlcpy(strresult, "Malloc failed for clear.data", 4040Sstevel@tonic-gate sizeof (strresult)); 4050Sstevel@tonic-gate } 4060Sstevel@tonic-gate 4070Sstevel@tonic-gate cipher.length = 0; 4080Sstevel@tonic-gate 4090Sstevel@tonic-gate if (ap_rep.length) { 4100Sstevel@tonic-gate if (ret = krb5_auth_con_setaddrs(context, auth_context, 4110Sstevel@tonic-gate &local_kaddr, NULL)) { 4120Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4130Sstevel@tonic-gate (void) strlcpy(strresult, 4140Sstevel@tonic-gate "Failed storing client and server internet addresses", 4150Sstevel@tonic-gate sizeof (strresult)); 4160Sstevel@tonic-gate } else { 4170Sstevel@tonic-gate if (ret = krb5_mk_priv(context, auth_context, &clear, 4180Sstevel@tonic-gate &cipher, &replay)) { 4190Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4200Sstevel@tonic-gate (void) strlcpy(strresult, 4210Sstevel@tonic-gate "Failed encrypting reply", 4220Sstevel@tonic-gate sizeof (strresult)); 4230Sstevel@tonic-gate } 4240Sstevel@tonic-gate } 4250Sstevel@tonic-gate } 4260Sstevel@tonic-gate 4270Sstevel@tonic-gate ptr = clear.data; 4280Sstevel@tonic-gate *ptr++ = (numresult>>8) & 0xff; 4290Sstevel@tonic-gate *ptr++ = numresult & 0xff; 4300Sstevel@tonic-gate 4310Sstevel@tonic-gate (void) memcpy(ptr, strresult, strlen(strresult)); 4320Sstevel@tonic-gate 4330Sstevel@tonic-gate /* 4340Sstevel@tonic-gate * If no KRB-PRIV was constructed, then we need a KRB-ERROR. 4350Sstevel@tonic-gate * If this fails, just bail. There's nothing else we can do. 4360Sstevel@tonic-gate */ 4370Sstevel@tonic-gate if (cipher.length == 0) { 4380Sstevel@tonic-gate /* 4390Sstevel@tonic-gate * Clear out ap_rep now, so that it won't be inserted 4400Sstevel@tonic-gate * in the reply 4410Sstevel@tonic-gate */ 4420Sstevel@tonic-gate if (ap_rep.length) { 4430Sstevel@tonic-gate if (ap_rep.data != NULL) 4440Sstevel@tonic-gate krb5_xfree(ap_rep.data); 4450Sstevel@tonic-gate ap_rep.data = NULL; 4460Sstevel@tonic-gate ap_rep.length = 0; 4470Sstevel@tonic-gate } 4480Sstevel@tonic-gate 4490Sstevel@tonic-gate krberror.ctime = 0; 4500Sstevel@tonic-gate krberror.cusec = 0; 4510Sstevel@tonic-gate krberror.susec = 0; 4520Sstevel@tonic-gate if (ret = krb5_timeofday(context, &krberror.stime)) 4530Sstevel@tonic-gate goto bailout; 4540Sstevel@tonic-gate 4550Sstevel@tonic-gate /* 4560Sstevel@tonic-gate * This is really icky. but it's what all the other callers 4570Sstevel@tonic-gate * to mk_error do. 4580Sstevel@tonic-gate */ 4590Sstevel@tonic-gate krberror.error = ret; 4600Sstevel@tonic-gate krberror.error -= ERROR_TABLE_BASE_krb5; 4610Sstevel@tonic-gate if (krberror.error < 0 || krberror.error > 128) 4620Sstevel@tonic-gate krberror.error = KRB_ERR_GENERIC; 4630Sstevel@tonic-gate 4640Sstevel@tonic-gate krberror.client = NULL; 4650Sstevel@tonic-gate if (ret = krb5_build_principal(context, &krberror.server, 4660Sstevel@tonic-gate strlen(realm), realm, 4670Sstevel@tonic-gate "kadmin", "changepw", NULL)) { 4680Sstevel@tonic-gate goto bailout; 4690Sstevel@tonic-gate } 4700Sstevel@tonic-gate 4710Sstevel@tonic-gate krberror.text.length = 0; 4720Sstevel@tonic-gate krberror.e_data = clear; 4730Sstevel@tonic-gate 4740Sstevel@tonic-gate ret = krb5_mk_error(context, &krberror, &cipher); 4750Sstevel@tonic-gate 4760Sstevel@tonic-gate krb5_free_principal(context, krberror.server); 4770Sstevel@tonic-gate 4780Sstevel@tonic-gate if (ret) 4790Sstevel@tonic-gate goto bailout; 4800Sstevel@tonic-gate } 4810Sstevel@tonic-gate 4820Sstevel@tonic-gate /* 4830Sstevel@tonic-gate * Construct the reply 4840Sstevel@tonic-gate */ 4850Sstevel@tonic-gate rep->length = 6 + ap_rep.length + cipher.length; 4860Sstevel@tonic-gate if ((rep->data = (char *)malloc(rep->length)) == NULL) { 4870Sstevel@tonic-gate ret = errno; 4880Sstevel@tonic-gate goto bailout; 4890Sstevel@tonic-gate } 4900Sstevel@tonic-gate ptr = rep->data; 4910Sstevel@tonic-gate 4920Sstevel@tonic-gate /* 4930Sstevel@tonic-gate * Length 4940Sstevel@tonic-gate */ 4950Sstevel@tonic-gate *ptr++ = (rep->length>>8) & 0xff; 4960Sstevel@tonic-gate *ptr++ = rep->length & 0xff; 4970Sstevel@tonic-gate 4980Sstevel@tonic-gate /* 4990Sstevel@tonic-gate * Version == 0x0001 big-endian 5000Sstevel@tonic-gate */ 5010Sstevel@tonic-gate *ptr++ = 0; 5020Sstevel@tonic-gate *ptr++ = 1; 5030Sstevel@tonic-gate 5040Sstevel@tonic-gate /* 5050Sstevel@tonic-gate * ap_rep length, big-endian 5060Sstevel@tonic-gate */ 5070Sstevel@tonic-gate *ptr++ = (ap_rep.length>>8) & 0xff; 5080Sstevel@tonic-gate *ptr++ = ap_rep.length & 0xff; 5090Sstevel@tonic-gate 5100Sstevel@tonic-gate /* 5110Sstevel@tonic-gate * ap-rep data 5120Sstevel@tonic-gate */ 5130Sstevel@tonic-gate if (ap_rep.length) { 5140Sstevel@tonic-gate (void) memcpy(ptr, ap_rep.data, ap_rep.length); 5150Sstevel@tonic-gate ptr += ap_rep.length; 5160Sstevel@tonic-gate } 5170Sstevel@tonic-gate 5180Sstevel@tonic-gate /* 5190Sstevel@tonic-gate * krb-priv or krb-error 5200Sstevel@tonic-gate */ 5210Sstevel@tonic-gate (void) memcpy(ptr, cipher.data, cipher.length); 5220Sstevel@tonic-gate 5230Sstevel@tonic-gate bailout: 5240Sstevel@tonic-gate if (auth_context) 5250Sstevel@tonic-gate krb5_auth_con_free(context, auth_context); 5260Sstevel@tonic-gate if (changepw) 5270Sstevel@tonic-gate krb5_free_principal(context, changepw); 5280Sstevel@tonic-gate if (ap_rep.data != NULL) 5290Sstevel@tonic-gate krb5_xfree(ap_rep.data); 5300Sstevel@tonic-gate if (ticket) 5310Sstevel@tonic-gate krb5_free_ticket(context, ticket); 5320Sstevel@tonic-gate if (clear.data != NULL) 5330Sstevel@tonic-gate krb5_xfree(clear.data); 5340Sstevel@tonic-gate if (cipher.data != NULL) 5350Sstevel@tonic-gate krb5_xfree(cipher.data); 5360Sstevel@tonic-gate if (allocated_mem) 5370Sstevel@tonic-gate krb5_xfree(local_kaddr.contents); 5380Sstevel@tonic-gate 5390Sstevel@tonic-gate return (ret); 5400Sstevel@tonic-gate } 5410Sstevel@tonic-gate 5420Sstevel@tonic-gate 5430Sstevel@tonic-gate /* 5440Sstevel@tonic-gate * This routine is used to handle password-change requests received 5450Sstevel@tonic-gate * on kpasswd-port 464 from MIT/M$ clients. 5460Sstevel@tonic-gate */ 5470Sstevel@tonic-gate void 5480Sstevel@tonic-gate handle_chpw(krb5_context context, int s1, 5490Sstevel@tonic-gate void *serverhandle, kadm5_config_params *params) 5500Sstevel@tonic-gate { 5510Sstevel@tonic-gate krb5_error_code ret; 5520Sstevel@tonic-gate char req[MAXAPREQ]; 5530Sstevel@tonic-gate int len; 5540Sstevel@tonic-gate struct sockaddr_in from; 5550Sstevel@tonic-gate int fromlen; 5560Sstevel@tonic-gate krb5_keytab kt; 5570Sstevel@tonic-gate krb5_data reqdata, repdata; 5580Sstevel@tonic-gate int s2 = -1; 5590Sstevel@tonic-gate 5600Sstevel@tonic-gate reqdata.length = 0; 5610Sstevel@tonic-gate reqdata.data = NULL; 5620Sstevel@tonic-gate repdata.length = 0; 5630Sstevel@tonic-gate repdata.data = NULL; 5640Sstevel@tonic-gate 5650Sstevel@tonic-gate fromlen = sizeof (from); 5660Sstevel@tonic-gate 5670Sstevel@tonic-gate if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from, 5680Sstevel@tonic-gate &fromlen)) < 0) { 5690Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive " 5700Sstevel@tonic-gate "request: %s"), error_message(errno)); 5710Sstevel@tonic-gate return; 5720Sstevel@tonic-gate } 5730Sstevel@tonic-gate 5740Sstevel@tonic-gate if ((ret = krb5_kt_resolve(context, params->admin_keytab, &kt))) { 5750Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open " 5760Sstevel@tonic-gate "admin keytab %s"), error_message(ret)); 5770Sstevel@tonic-gate return; 5780Sstevel@tonic-gate } 5790Sstevel@tonic-gate 5800Sstevel@tonic-gate reqdata.length = len; 5810Sstevel@tonic-gate reqdata.data = req; 5820Sstevel@tonic-gate 5830Sstevel@tonic-gate /* 5840Sstevel@tonic-gate * This is really obscure. s1 is used for all communications. it 5850Sstevel@tonic-gate * is left unconnected in case the server is multihomed and routes 5860Sstevel@tonic-gate * are asymmetric. s2 is connected to resolve routes and get 5870Sstevel@tonic-gate * addresses. this is the *only* way to get proper addresses for 5880Sstevel@tonic-gate * multihomed hosts if routing is asymmetric. 5890Sstevel@tonic-gate * 5900Sstevel@tonic-gate * A related problem in the server, but not the client, is that 5910Sstevel@tonic-gate * many os's have no way to disconnect a connected udp socket, so 5920Sstevel@tonic-gate * the s2 socket needs to be closed and recreated for each 5930Sstevel@tonic-gate * request. The s1 socket must not be closed, or else queued 5940Sstevel@tonic-gate * requests will be lost. 5950Sstevel@tonic-gate * 5960Sstevel@tonic-gate * A "naive" client implementation (one socket, no connect, 5970Sstevel@tonic-gate * hostname resolution to get the local ip addr) will work and 5980Sstevel@tonic-gate * interoperate if the client is single-homed. 5990Sstevel@tonic-gate */ 6000Sstevel@tonic-gate 6010Sstevel@tonic-gate if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 6020Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create " 6030Sstevel@tonic-gate "connecting socket: %s"), error_message(errno)); 6040Sstevel@tonic-gate goto cleanup; 6050Sstevel@tonic-gate } 6060Sstevel@tonic-gate 6070Sstevel@tonic-gate if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) { 6080Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect " 6090Sstevel@tonic-gate "to client: %s"), error_message(errno)); 6100Sstevel@tonic-gate if (s2 > 0) 6110Sstevel@tonic-gate (void) close(s2); 6120Sstevel@tonic-gate goto cleanup; 6130Sstevel@tonic-gate } 6140Sstevel@tonic-gate 6150Sstevel@tonic-gate if ((ret = process_chpw_request(context, serverhandle, 6160Sstevel@tonic-gate params->realm, s2, kt, &from, 6170Sstevel@tonic-gate &reqdata, &repdata))) { 6180Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing " 6190Sstevel@tonic-gate "request: %s"), error_message(ret)); 6200Sstevel@tonic-gate } 6210Sstevel@tonic-gate 6220Sstevel@tonic-gate if (s2 > 0) 6230Sstevel@tonic-gate (void) close(s2); 6240Sstevel@tonic-gate 6250Sstevel@tonic-gate if (repdata.length == 0 || repdata.data == NULL) { 6260Sstevel@tonic-gate /* 6270Sstevel@tonic-gate * Just return. This means something really bad happened 6280Sstevel@tonic-gate */ 6290Sstevel@tonic-gate goto cleanup; 6300Sstevel@tonic-gate } 6310Sstevel@tonic-gate 6320Sstevel@tonic-gate len = sendto(s1, repdata.data, repdata.length, 0, 6330Sstevel@tonic-gate (struct sockaddr *)&from, sizeof (from)); 6340Sstevel@tonic-gate 6350Sstevel@tonic-gate if (len < repdata.length) { 6360Sstevel@tonic-gate krb5_xfree(repdata.data); 6370Sstevel@tonic-gate 6380Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:" 6390Sstevel@tonic-gate " %s"), error_message(errno)); 6400Sstevel@tonic-gate goto cleanup; 6410Sstevel@tonic-gate } 6420Sstevel@tonic-gate 6430Sstevel@tonic-gate if (repdata.data != NULL) 6440Sstevel@tonic-gate krb5_xfree(repdata.data); 6450Sstevel@tonic-gate cleanup: 6460Sstevel@tonic-gate krb5_kt_close(context, kt); 6470Sstevel@tonic-gate } 648