17c478bd9Sstevel@tonic-gate /* 2*46c8d03dSMark Phalan * Copyright 2010 Sun Microsystems, Inc. All rights reserved. 37c478bd9Sstevel@tonic-gate * Use is subject to license terms. 47c478bd9Sstevel@tonic-gate */ 57c478bd9Sstevel@tonic-gate 67c478bd9Sstevel@tonic-gate /* 77c478bd9Sstevel@tonic-gate * lib/krb5/kadm5/srv/chgpwd.c 87c478bd9Sstevel@tonic-gate * 97c478bd9Sstevel@tonic-gate * Copyright 1998 by the Massachusetts Institute of Technology. 107c478bd9Sstevel@tonic-gate * All Rights Reserved. 117c478bd9Sstevel@tonic-gate * 127c478bd9Sstevel@tonic-gate * Export of this software from the United States of America may 137c478bd9Sstevel@tonic-gate * require a specific license from the United States Government. 147c478bd9Sstevel@tonic-gate * It is the responsibility of any person or organization contemplating 157c478bd9Sstevel@tonic-gate * export to obtain such a license before exporting. 167c478bd9Sstevel@tonic-gate * 177c478bd9Sstevel@tonic-gate * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and 187c478bd9Sstevel@tonic-gate * distribute this software and its documentation for any purpose and 197c478bd9Sstevel@tonic-gate * without fee is hereby granted, provided that the above copyright 207c478bd9Sstevel@tonic-gate * notice appear in all copies and that both that copyright notice and 217c478bd9Sstevel@tonic-gate * this permission notice appear in supporting documentation, and that 227c478bd9Sstevel@tonic-gate * the name of M.I.T. not be used in advertising or publicity pertaining 237c478bd9Sstevel@tonic-gate * to distribution of the software without specific, written prior 247c478bd9Sstevel@tonic-gate * permission. Furthermore if you modify this software you must label 257c478bd9Sstevel@tonic-gate * your software as modified software and not distribute it in such a 267c478bd9Sstevel@tonic-gate * fashion that it might be confused with the original M.I.T. software. 277c478bd9Sstevel@tonic-gate * M.I.T. makes no representations about the suitability of 287c478bd9Sstevel@tonic-gate * this software for any purpose. It is provided "as is" without express 297c478bd9Sstevel@tonic-gate * or implied warranty. 307c478bd9Sstevel@tonic-gate * 317c478bd9Sstevel@tonic-gate */ 327c478bd9Sstevel@tonic-gate 337c478bd9Sstevel@tonic-gate /* 347c478bd9Sstevel@tonic-gate * chgpwd.c - Handles changepw requests issued from non-Solaris krb5 clients. 357c478bd9Sstevel@tonic-gate */ 367c478bd9Sstevel@tonic-gate 377c478bd9Sstevel@tonic-gate #include <libintl.h> 387c478bd9Sstevel@tonic-gate #include <locale.h> 397c478bd9Sstevel@tonic-gate #include <kadm5/admin.h> 407c478bd9Sstevel@tonic-gate #include <syslog.h> 417c478bd9Sstevel@tonic-gate #include <krb5/adm_proto.h> 427c478bd9Sstevel@tonic-gate 437c478bd9Sstevel@tonic-gate #define MAXAPREQ 1500 447c478bd9Sstevel@tonic-gate 457c478bd9Sstevel@tonic-gate static krb5_error_code 467c478bd9Sstevel@tonic-gate process_chpw_request(krb5_context context, void *server_handle, 477c478bd9Sstevel@tonic-gate char *realm, int s, krb5_keytab keytab, 487c478bd9Sstevel@tonic-gate struct sockaddr_in *sin, krb5_data *req, 497c478bd9Sstevel@tonic-gate krb5_data *rep) 507c478bd9Sstevel@tonic-gate { 517c478bd9Sstevel@tonic-gate krb5_error_code ret; 527c478bd9Sstevel@tonic-gate char *ptr; 537c478bd9Sstevel@tonic-gate int plen, vno; 547c478bd9Sstevel@tonic-gate krb5_address local_kaddr, remote_kaddr; 557c478bd9Sstevel@tonic-gate int allocated_mem = 0; 567c478bd9Sstevel@tonic-gate krb5_data ap_req, ap_rep; 577c478bd9Sstevel@tonic-gate krb5_auth_context auth_context; 587c478bd9Sstevel@tonic-gate krb5_principal changepw; 597c478bd9Sstevel@tonic-gate krb5_ticket *ticket; 607c478bd9Sstevel@tonic-gate krb5_data cipher, clear; 617c478bd9Sstevel@tonic-gate struct sockaddr local_addr, remote_addr; 627c478bd9Sstevel@tonic-gate int addrlen; 637c478bd9Sstevel@tonic-gate krb5_replay_data replay; 647c478bd9Sstevel@tonic-gate krb5_error krberror; 657c478bd9Sstevel@tonic-gate int numresult; 667c478bd9Sstevel@tonic-gate char strresult[1024]; 672278144aSsemery char *clientstr; 6846736d35Ssemery size_t clen; 6946736d35Ssemery char *cdots; 707c478bd9Sstevel@tonic-gate 717c478bd9Sstevel@tonic-gate ret = 0; 727c478bd9Sstevel@tonic-gate rep->length = 0; 737c478bd9Sstevel@tonic-gate 747c478bd9Sstevel@tonic-gate auth_context = NULL; 757c478bd9Sstevel@tonic-gate changepw = NULL; 767c478bd9Sstevel@tonic-gate ap_rep.length = 0; 777c478bd9Sstevel@tonic-gate ap_rep.data = NULL; 787c478bd9Sstevel@tonic-gate ticket = NULL; 797c478bd9Sstevel@tonic-gate clear.length = 0; 807c478bd9Sstevel@tonic-gate clear.data = NULL; 817c478bd9Sstevel@tonic-gate cipher.length = 0; 827c478bd9Sstevel@tonic-gate cipher.data = NULL; 837c478bd9Sstevel@tonic-gate 847c478bd9Sstevel@tonic-gate if (req->length < 4) { 857c478bd9Sstevel@tonic-gate /* 867c478bd9Sstevel@tonic-gate * either this, or the server is printing bad messages, 877c478bd9Sstevel@tonic-gate * or the caller passed in garbage 887c478bd9Sstevel@tonic-gate */ 897c478bd9Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED; 907c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED; 917c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "Request was truncated", 927c478bd9Sstevel@tonic-gate sizeof (strresult)); 937c478bd9Sstevel@tonic-gate goto chpwfail; 947c478bd9Sstevel@tonic-gate } 957c478bd9Sstevel@tonic-gate 967c478bd9Sstevel@tonic-gate ptr = req->data; 977c478bd9Sstevel@tonic-gate 987c478bd9Sstevel@tonic-gate /* 997c478bd9Sstevel@tonic-gate * Verify length 1007c478bd9Sstevel@tonic-gate */ 1017c478bd9Sstevel@tonic-gate plen = (*ptr++ & 0xff); 1027c478bd9Sstevel@tonic-gate plen = (plen<<8) | (*ptr++ & 0xff); 1037c478bd9Sstevel@tonic-gate 1047c478bd9Sstevel@tonic-gate if (plen != req->length) 1057c478bd9Sstevel@tonic-gate return (KRB5KRB_AP_ERR_MODIFIED); 1067c478bd9Sstevel@tonic-gate 1077c478bd9Sstevel@tonic-gate /* 1087c478bd9Sstevel@tonic-gate * Verify version number 1097c478bd9Sstevel@tonic-gate */ 1107c478bd9Sstevel@tonic-gate vno = (*ptr++ & 0xff); 1117c478bd9Sstevel@tonic-gate vno = (vno<<8) | (*ptr++ & 0xff); 1127c478bd9Sstevel@tonic-gate 1137c478bd9Sstevel@tonic-gate if (vno != 1) { 1147c478bd9Sstevel@tonic-gate ret = KRB5KDC_ERR_BAD_PVNO; 1157c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED; 1167c478bd9Sstevel@tonic-gate (void) snprintf(strresult, sizeof (strresult), 1177c478bd9Sstevel@tonic-gate "Request contained unknown protocol version number %d", 1187c478bd9Sstevel@tonic-gate vno); 1197c478bd9Sstevel@tonic-gate goto chpwfail; 1207c478bd9Sstevel@tonic-gate } 1217c478bd9Sstevel@tonic-gate 1227c478bd9Sstevel@tonic-gate /* 1237c478bd9Sstevel@tonic-gate * Read, check ap-req length 1247c478bd9Sstevel@tonic-gate */ 1257c478bd9Sstevel@tonic-gate ap_req.length = (*ptr++ & 0xff); 1267c478bd9Sstevel@tonic-gate ap_req.length = (ap_req.length<<8) | (*ptr++ & 0xff); 1277c478bd9Sstevel@tonic-gate 1287c478bd9Sstevel@tonic-gate if (ptr + ap_req.length >= req->data + req->length) { 1297c478bd9Sstevel@tonic-gate ret = KRB5KRB_AP_ERR_MODIFIED; 1307c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_MALFORMED; 1317c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "Request was truncated in AP-REQ", 1327c478bd9Sstevel@tonic-gate sizeof (strresult)); 1337c478bd9Sstevel@tonic-gate goto chpwfail; 1347c478bd9Sstevel@tonic-gate } 1357c478bd9Sstevel@tonic-gate 1367c478bd9Sstevel@tonic-gate /* 1377c478bd9Sstevel@tonic-gate * Verify ap_req 1387c478bd9Sstevel@tonic-gate */ 1397c478bd9Sstevel@tonic-gate ap_req.data = ptr; 1407c478bd9Sstevel@tonic-gate ptr += ap_req.length; 1417c478bd9Sstevel@tonic-gate 1427c478bd9Sstevel@tonic-gate if (ret = krb5_auth_con_init(context, &auth_context)) { 143a0709436Smp153739 krb5_klog_syslog(LOG_ERR, 144a0709436Smp153739 gettext("Change password request failed. " 145a0709436Smp153739 "Failed initializing auth context: %s"), 146a0709436Smp153739 error_message(ret)); 1477c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 1487c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "Failed initializing auth context", 1497c478bd9Sstevel@tonic-gate sizeof (strresult)); 1507c478bd9Sstevel@tonic-gate goto chpwfail; 1517c478bd9Sstevel@tonic-gate } 1527c478bd9Sstevel@tonic-gate 1537c478bd9Sstevel@tonic-gate if (ret = krb5_auth_con_setflags(context, auth_context, 1547c478bd9Sstevel@tonic-gate KRB5_AUTH_CONTEXT_DO_SEQUENCE)) { 155a0709436Smp153739 krb5_klog_syslog(LOG_ERR, 156a0709436Smp153739 gettext("Change password request failed. " 157a0709436Smp153739 "Failed setting auth " 158a0709436Smp153739 "context flags: %s"), 159a0709436Smp153739 error_message(ret)); 1607c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 1617c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "Failed initializing auth context", 1627c478bd9Sstevel@tonic-gate sizeof (strresult)); 1637c478bd9Sstevel@tonic-gate goto chpwfail; 1647c478bd9Sstevel@tonic-gate } 1657c478bd9Sstevel@tonic-gate 1667c478bd9Sstevel@tonic-gate if (ret = krb5_build_principal(context, &changepw, strlen(realm), realm, 1677c478bd9Sstevel@tonic-gate "kadmin", "changepw", NULL)) { 168a0709436Smp153739 krb5_klog_syslog(LOG_ERR, 169a0709436Smp153739 gettext("Change password request failed " 170a0709436Smp153739 "Failed to build kadmin/changepw " 171a0709436Smp153739 "principal: %s"), 172a0709436Smp153739 error_message(ret)); 1737c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 1747c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 1757c478bd9Sstevel@tonic-gate "Failed building kadmin/changepw principal", 1767c478bd9Sstevel@tonic-gate sizeof (strresult)); 1777c478bd9Sstevel@tonic-gate goto chpwfail; 1787c478bd9Sstevel@tonic-gate } 1797c478bd9Sstevel@tonic-gate 1807c478bd9Sstevel@tonic-gate ret = krb5_rd_req(context, &auth_context, &ap_req, changepw, keytab, 1817c478bd9Sstevel@tonic-gate NULL, &ticket); 1827c478bd9Sstevel@tonic-gate 1837c478bd9Sstevel@tonic-gate if (ret) { 184a0709436Smp153739 char kt_name[MAX_KEYTAB_NAME_LEN]; 185a0709436Smp153739 if (krb5_kt_get_name(context, keytab, 186a0709436Smp153739 kt_name, sizeof (kt_name))) 187a0709436Smp153739 strncpy(kt_name, "default keytab", sizeof (kt_name)); 188a0709436Smp153739 189a0709436Smp153739 switch (ret) { 190a0709436Smp153739 case KRB5_KT_NOTFOUND: 191a0709436Smp153739 krb5_klog_syslog(LOG_ERR, 192a0709436Smp153739 gettext("Change password request failed because " 193a0709436Smp153739 "keytab entry \"kadmin/changepw\" " 194a0709436Smp153739 "is missing from \"%s\""), 195a0709436Smp153739 kt_name); 196a0709436Smp153739 break; 197a0709436Smp153739 case ENOENT: 198a0709436Smp153739 krb5_klog_syslog(LOG_ERR, 199a0709436Smp153739 gettext("Change password request failed because " 200a0709436Smp153739 "keytab file \"%s\" does not exist"), 201a0709436Smp153739 kt_name); 202a0709436Smp153739 break; 203a0709436Smp153739 default: 204a0709436Smp153739 krb5_klog_syslog(LOG_ERR, 205a0709436Smp153739 gettext("Change password request failed. " 206a0709436Smp153739 "Failed to parse Kerberos AP_REQ message: %s"), 207a0709436Smp153739 error_message(ret)); 208a0709436Smp153739 } 209a0709436Smp153739 2107c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR; 2117c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "Failed reading application request", 2127c478bd9Sstevel@tonic-gate sizeof (strresult)); 2137c478bd9Sstevel@tonic-gate goto chpwfail; 2147c478bd9Sstevel@tonic-gate } 2157c478bd9Sstevel@tonic-gate 2167c478bd9Sstevel@tonic-gate /* 2177c478bd9Sstevel@tonic-gate * Set up address info 2187c478bd9Sstevel@tonic-gate */ 2197c478bd9Sstevel@tonic-gate addrlen = sizeof (local_addr); 2207c478bd9Sstevel@tonic-gate 2217c478bd9Sstevel@tonic-gate if (getsockname(s, &local_addr, &addrlen) < 0) { 2227c478bd9Sstevel@tonic-gate ret = errno; 2237c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 2247c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 2257c478bd9Sstevel@tonic-gate "Failed getting server internet address", 2267c478bd9Sstevel@tonic-gate sizeof (strresult)); 2277c478bd9Sstevel@tonic-gate goto chpwfail; 2287c478bd9Sstevel@tonic-gate } 2297c478bd9Sstevel@tonic-gate 2307c478bd9Sstevel@tonic-gate /* 2317c478bd9Sstevel@tonic-gate * Some brain-dead OS's don't return useful information from 2327c478bd9Sstevel@tonic-gate * the getsockname call. Namely, windows and solaris. 2337c478bd9Sstevel@tonic-gate */ 2347c478bd9Sstevel@tonic-gate if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) { 2357c478bd9Sstevel@tonic-gate local_kaddr.addrtype = ADDRTYPE_INET; 2367c478bd9Sstevel@tonic-gate local_kaddr.length = sizeof (((struct sockaddr_in *) 2377c478bd9Sstevel@tonic-gate &local_addr)->sin_addr); 2387c478bd9Sstevel@tonic-gate /* CSTYLED */ 2397c478bd9Sstevel@tonic-gate local_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&local_addr)->sin_addr); 2407c478bd9Sstevel@tonic-gate } else { 2417c478bd9Sstevel@tonic-gate krb5_address **addrs; 2427c478bd9Sstevel@tonic-gate 2437c478bd9Sstevel@tonic-gate krb5_os_localaddr(context, &addrs); 2447c478bd9Sstevel@tonic-gate 2457c478bd9Sstevel@tonic-gate local_kaddr.magic = addrs[0]->magic; 2467c478bd9Sstevel@tonic-gate local_kaddr.addrtype = addrs[0]->addrtype; 2477c478bd9Sstevel@tonic-gate local_kaddr.length = addrs[0]->length; 2487c478bd9Sstevel@tonic-gate if ((local_kaddr.contents = malloc(addrs[0]->length)) == 0) { 2497c478bd9Sstevel@tonic-gate ret = errno; 2507c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 2517c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 2527c478bd9Sstevel@tonic-gate "Malloc failed for local_kaddr", 2537c478bd9Sstevel@tonic-gate sizeof (strresult)); 2547c478bd9Sstevel@tonic-gate goto chpwfail; 2557c478bd9Sstevel@tonic-gate } 2567c478bd9Sstevel@tonic-gate 2577c478bd9Sstevel@tonic-gate (void) memcpy(local_kaddr.contents, addrs[0]->contents, 2587c478bd9Sstevel@tonic-gate addrs[0]->length); 2597c478bd9Sstevel@tonic-gate allocated_mem++; 2607c478bd9Sstevel@tonic-gate 2617c478bd9Sstevel@tonic-gate krb5_free_addresses(context, addrs); 2627c478bd9Sstevel@tonic-gate } 2637c478bd9Sstevel@tonic-gate 2647c478bd9Sstevel@tonic-gate addrlen = sizeof (remote_addr); 2657c478bd9Sstevel@tonic-gate 2667c478bd9Sstevel@tonic-gate if (getpeername(s, &remote_addr, &addrlen) < 0) { 2677c478bd9Sstevel@tonic-gate ret = errno; 2687c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 2697c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 2707c478bd9Sstevel@tonic-gate "Failed getting client internet address", 2717c478bd9Sstevel@tonic-gate sizeof (strresult)); 2727c478bd9Sstevel@tonic-gate goto chpwfail; 2737c478bd9Sstevel@tonic-gate } 2747c478bd9Sstevel@tonic-gate 2757c478bd9Sstevel@tonic-gate remote_kaddr.addrtype = ADDRTYPE_INET; 2767c478bd9Sstevel@tonic-gate remote_kaddr.length = sizeof (((struct sockaddr_in *) 2777c478bd9Sstevel@tonic-gate &remote_addr)->sin_addr); 2787c478bd9Sstevel@tonic-gate /* CSTYLED */ 2797c478bd9Sstevel@tonic-gate remote_kaddr.contents = (krb5_octet *) &(((struct sockaddr_in *)&remote_addr)->sin_addr); 2807c478bd9Sstevel@tonic-gate remote_kaddr.addrtype = ADDRTYPE_INET; 2817c478bd9Sstevel@tonic-gate remote_kaddr.length = sizeof (sin->sin_addr); 2827c478bd9Sstevel@tonic-gate remote_kaddr.contents = (krb5_octet *) &sin->sin_addr; 2837c478bd9Sstevel@tonic-gate 2847c478bd9Sstevel@tonic-gate /* 2857c478bd9Sstevel@tonic-gate * mk_priv requires that the local address be set. 2867c478bd9Sstevel@tonic-gate * getsockname is used for this. rd_priv requires that the 2877c478bd9Sstevel@tonic-gate * remote address be set. recvfrom is used for this. If 2887c478bd9Sstevel@tonic-gate * rd_priv is given a local address, and the message has the 2897c478bd9Sstevel@tonic-gate * recipient addr in it, this will be checked. However, there 2907c478bd9Sstevel@tonic-gate * is simply no way to know ahead of time what address the 2917c478bd9Sstevel@tonic-gate * message will be delivered *to*. Therefore, it is important 2927c478bd9Sstevel@tonic-gate * that either no recipient address is in the messages when 2937c478bd9Sstevel@tonic-gate * mk_priv is called, or that no local address is passed to 2947c478bd9Sstevel@tonic-gate * rd_priv. Both is a better idea, and I have done that. In 2957c478bd9Sstevel@tonic-gate * summary, when mk_priv is called, *only* a local address is 2967c478bd9Sstevel@tonic-gate * specified. when rd_priv is called, *only* a remote address 2977c478bd9Sstevel@tonic-gate * is specified. Are we having fun yet? 2987c478bd9Sstevel@tonic-gate */ 2997c478bd9Sstevel@tonic-gate if (ret = krb5_auth_con_setaddrs(context, auth_context, NULL, 3007c478bd9Sstevel@tonic-gate &remote_kaddr)) { 3017c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3027c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 3037c478bd9Sstevel@tonic-gate "Failed storing client internet address", 3047c478bd9Sstevel@tonic-gate sizeof (strresult)); 3057c478bd9Sstevel@tonic-gate goto chpwfail; 3067c478bd9Sstevel@tonic-gate } 3077c478bd9Sstevel@tonic-gate 3087c478bd9Sstevel@tonic-gate /* 3097c478bd9Sstevel@tonic-gate * Verify that this is an AS_REQ ticket 3107c478bd9Sstevel@tonic-gate */ 3117c478bd9Sstevel@tonic-gate if (!(ticket->enc_part2->flags & TKT_FLG_INITIAL)) { 3127c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR; 3137c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 3147c478bd9Sstevel@tonic-gate "Ticket must be derived from a password", 3157c478bd9Sstevel@tonic-gate sizeof (strresult)); 3167c478bd9Sstevel@tonic-gate goto chpwfail; 3177c478bd9Sstevel@tonic-gate } 3187c478bd9Sstevel@tonic-gate 3197c478bd9Sstevel@tonic-gate /* 3207c478bd9Sstevel@tonic-gate * Construct the ap-rep 3217c478bd9Sstevel@tonic-gate */ 3227c478bd9Sstevel@tonic-gate if (ret = krb5_mk_rep(context, auth_context, &ap_rep)) { 3237c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_AUTHERROR; 3247c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 3257c478bd9Sstevel@tonic-gate "Failed replying to application request", 3267c478bd9Sstevel@tonic-gate sizeof (strresult)); 3277c478bd9Sstevel@tonic-gate goto chpwfail; 3287c478bd9Sstevel@tonic-gate } 3297c478bd9Sstevel@tonic-gate 3307c478bd9Sstevel@tonic-gate /* 3317c478bd9Sstevel@tonic-gate * Decrypt the new password 3327c478bd9Sstevel@tonic-gate */ 3337c478bd9Sstevel@tonic-gate cipher.length = (req->data + req->length) - ptr; 3347c478bd9Sstevel@tonic-gate cipher.data = ptr; 3357c478bd9Sstevel@tonic-gate 3367c478bd9Sstevel@tonic-gate if (ret = krb5_rd_priv(context, auth_context, &cipher, 3377c478bd9Sstevel@tonic-gate &clear, &replay)) { 3387c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3397c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "Failed decrypting request", 3407c478bd9Sstevel@tonic-gate sizeof (strresult)); 3417c478bd9Sstevel@tonic-gate goto chpwfail; 3427c478bd9Sstevel@tonic-gate } 3437c478bd9Sstevel@tonic-gate 3442278144aSsemery ret = krb5_unparse_name(context, ticket->enc_part2->client, &clientstr); 3452278144aSsemery if (ret) { 3462278144aSsemery numresult = KRB5_KPASSWD_HARDERROR; 3472278144aSsemery (void) strcpy(strresult, "Failed decrypting request"); 3482278144aSsemery goto chpwfail; 3492278144aSsemery } 3502278144aSsemery 3517c478bd9Sstevel@tonic-gate /* 3527c478bd9Sstevel@tonic-gate * Change the password 3537c478bd9Sstevel@tonic-gate */ 3547c478bd9Sstevel@tonic-gate if ((ptr = (char *)malloc(clear.length + 1)) == NULL) { 3557c478bd9Sstevel@tonic-gate ret = errno; 3567c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3577c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "Malloc failed for ptr", 3587c478bd9Sstevel@tonic-gate sizeof (strresult)); 3592278144aSsemery krb5_free_unparsed_name(context, clientstr); 3607c478bd9Sstevel@tonic-gate goto chpwfail; 3617c478bd9Sstevel@tonic-gate } 3627c478bd9Sstevel@tonic-gate (void) memcpy(ptr, clear.data, clear.length); 3637c478bd9Sstevel@tonic-gate ptr[clear.length] = '\0'; 3647c478bd9Sstevel@tonic-gate 3657c478bd9Sstevel@tonic-gate ret = (kadm5_ret_t)kadm5_chpass_principal_util(server_handle, 3667c478bd9Sstevel@tonic-gate ticket->enc_part2->client, 3677c478bd9Sstevel@tonic-gate ptr, NULL, strresult, 3687c478bd9Sstevel@tonic-gate sizeof (strresult)); 3692278144aSsemery 3707c478bd9Sstevel@tonic-gate /* 3717c478bd9Sstevel@tonic-gate * Zap the password 3727c478bd9Sstevel@tonic-gate */ 3737c478bd9Sstevel@tonic-gate (void) memset(clear.data, 0, clear.length); 3747c478bd9Sstevel@tonic-gate (void) memset(ptr, 0, clear.length); 3757c478bd9Sstevel@tonic-gate if (clear.data != NULL) { 3767c478bd9Sstevel@tonic-gate krb5_xfree(clear.data); 3777c478bd9Sstevel@tonic-gate clear.data = NULL; 3787c478bd9Sstevel@tonic-gate } 3797c478bd9Sstevel@tonic-gate free(ptr); 3807c478bd9Sstevel@tonic-gate clear.length = 0; 3817c478bd9Sstevel@tonic-gate 38246736d35Ssemery clen = strlen(clientstr); 38346736d35Ssemery trunc_name(&clen, &cdots); 38446736d35Ssemery krb5_klog_syslog(LOG_NOTICE, "chpw request from %s for %.*s%s: %s", 3852278144aSsemery inet_ntoa(((struct sockaddr_in *)&remote_addr)->sin_addr), 38646736d35Ssemery clen, clientstr, cdots, ret ? error_message(ret) : "success"); 3872278144aSsemery krb5_free_unparsed_name(context, clientstr); 3882278144aSsemery 3897c478bd9Sstevel@tonic-gate if (ret) { 3907c478bd9Sstevel@tonic-gate if ((ret != KADM5_PASS_Q_TOOSHORT) && 3917c478bd9Sstevel@tonic-gate (ret != KADM5_PASS_REUSE) && 3927c478bd9Sstevel@tonic-gate (ret != KADM5_PASS_Q_CLASS) && 3937c478bd9Sstevel@tonic-gate (ret != KADM5_PASS_Q_DICT) && 3947c478bd9Sstevel@tonic-gate (ret != KADM5_PASS_TOOSOON)) 3957c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 3967c478bd9Sstevel@tonic-gate else 3977c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_SOFTERROR; 3987c478bd9Sstevel@tonic-gate /* 3997c478bd9Sstevel@tonic-gate * strresult set by kadb5_chpass_principal_util() 4007c478bd9Sstevel@tonic-gate */ 4017c478bd9Sstevel@tonic-gate goto chpwfail; 4027c478bd9Sstevel@tonic-gate } 4037c478bd9Sstevel@tonic-gate 4047c478bd9Sstevel@tonic-gate /* 4057c478bd9Sstevel@tonic-gate * Success! 4067c478bd9Sstevel@tonic-gate */ 4077c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_SUCCESS; 4087c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "", sizeof (strresult)); 4097c478bd9Sstevel@tonic-gate 4107c478bd9Sstevel@tonic-gate chpwfail: 4117c478bd9Sstevel@tonic-gate 4127c478bd9Sstevel@tonic-gate clear.length = 2 + strlen(strresult); 4137c478bd9Sstevel@tonic-gate if (clear.data != NULL) { 4147c478bd9Sstevel@tonic-gate krb5_xfree(clear.data); 4157c478bd9Sstevel@tonic-gate clear.data = NULL; 4167c478bd9Sstevel@tonic-gate } 4177c478bd9Sstevel@tonic-gate if ((clear.data = (char *)malloc(clear.length)) == NULL) { 4187c478bd9Sstevel@tonic-gate ret = errno; 4197c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4207c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, "Malloc failed for clear.data", 4217c478bd9Sstevel@tonic-gate sizeof (strresult)); 4227c478bd9Sstevel@tonic-gate } 4237c478bd9Sstevel@tonic-gate 4242278144aSsemery ptr = clear.data; 4252278144aSsemery 4262278144aSsemery *ptr++ = (numresult>>8) & 0xff; 4272278144aSsemery *ptr++ = numresult & 0xff; 4282278144aSsemery 4292278144aSsemery (void) memcpy(ptr, strresult, strlen(strresult)); 4302278144aSsemery 4317c478bd9Sstevel@tonic-gate cipher.length = 0; 4327c478bd9Sstevel@tonic-gate 4337c478bd9Sstevel@tonic-gate if (ap_rep.length) { 4347c478bd9Sstevel@tonic-gate if (ret = krb5_auth_con_setaddrs(context, auth_context, 4357c478bd9Sstevel@tonic-gate &local_kaddr, NULL)) { 4367c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4377c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 4387c478bd9Sstevel@tonic-gate "Failed storing client and server internet addresses", 4397c478bd9Sstevel@tonic-gate sizeof (strresult)); 4407c478bd9Sstevel@tonic-gate } else { 4417c478bd9Sstevel@tonic-gate if (ret = krb5_mk_priv(context, auth_context, &clear, 4427c478bd9Sstevel@tonic-gate &cipher, &replay)) { 4437c478bd9Sstevel@tonic-gate numresult = KRB5_KPASSWD_HARDERROR; 4447c478bd9Sstevel@tonic-gate (void) strlcpy(strresult, 4457c478bd9Sstevel@tonic-gate "Failed encrypting reply", 4467c478bd9Sstevel@tonic-gate sizeof (strresult)); 4477c478bd9Sstevel@tonic-gate } 4487c478bd9Sstevel@tonic-gate } 4497c478bd9Sstevel@tonic-gate } 4507c478bd9Sstevel@tonic-gate 4517c478bd9Sstevel@tonic-gate /* 4527c478bd9Sstevel@tonic-gate * If no KRB-PRIV was constructed, then we need a KRB-ERROR. 4537c478bd9Sstevel@tonic-gate * If this fails, just bail. There's nothing else we can do. 4547c478bd9Sstevel@tonic-gate */ 4557c478bd9Sstevel@tonic-gate if (cipher.length == 0) { 4567c478bd9Sstevel@tonic-gate /* 4577c478bd9Sstevel@tonic-gate * Clear out ap_rep now, so that it won't be inserted 4587c478bd9Sstevel@tonic-gate * in the reply 4597c478bd9Sstevel@tonic-gate */ 4607c478bd9Sstevel@tonic-gate if (ap_rep.length) { 4617c478bd9Sstevel@tonic-gate if (ap_rep.data != NULL) 4627c478bd9Sstevel@tonic-gate krb5_xfree(ap_rep.data); 4637c478bd9Sstevel@tonic-gate ap_rep.data = NULL; 4647c478bd9Sstevel@tonic-gate ap_rep.length = 0; 4657c478bd9Sstevel@tonic-gate } 4667c478bd9Sstevel@tonic-gate 4677c478bd9Sstevel@tonic-gate krberror.ctime = 0; 4687c478bd9Sstevel@tonic-gate krberror.cusec = 0; 4697c478bd9Sstevel@tonic-gate krberror.susec = 0; 4707c478bd9Sstevel@tonic-gate if (ret = krb5_timeofday(context, &krberror.stime)) 4717c478bd9Sstevel@tonic-gate goto bailout; 4727c478bd9Sstevel@tonic-gate 4737c478bd9Sstevel@tonic-gate /* 4747c478bd9Sstevel@tonic-gate * This is really icky. but it's what all the other callers 4757c478bd9Sstevel@tonic-gate * to mk_error do. 4767c478bd9Sstevel@tonic-gate */ 4777c478bd9Sstevel@tonic-gate krberror.error = ret; 4787c478bd9Sstevel@tonic-gate krberror.error -= ERROR_TABLE_BASE_krb5; 4797c478bd9Sstevel@tonic-gate if (krberror.error < 0 || krberror.error > 128) 4807c478bd9Sstevel@tonic-gate krberror.error = KRB_ERR_GENERIC; 4817c478bd9Sstevel@tonic-gate 4827c478bd9Sstevel@tonic-gate krberror.client = NULL; 4837c478bd9Sstevel@tonic-gate if (ret = krb5_build_principal(context, &krberror.server, 4847c478bd9Sstevel@tonic-gate strlen(realm), realm, 4857c478bd9Sstevel@tonic-gate "kadmin", "changepw", NULL)) { 4867c478bd9Sstevel@tonic-gate goto bailout; 4877c478bd9Sstevel@tonic-gate } 4887c478bd9Sstevel@tonic-gate 4897c478bd9Sstevel@tonic-gate krberror.text.length = 0; 4907c478bd9Sstevel@tonic-gate krberror.e_data = clear; 4917c478bd9Sstevel@tonic-gate 4927c478bd9Sstevel@tonic-gate ret = krb5_mk_error(context, &krberror, &cipher); 4937c478bd9Sstevel@tonic-gate 4947c478bd9Sstevel@tonic-gate krb5_free_principal(context, krberror.server); 4957c478bd9Sstevel@tonic-gate 4967c478bd9Sstevel@tonic-gate if (ret) 4977c478bd9Sstevel@tonic-gate goto bailout; 4987c478bd9Sstevel@tonic-gate } 4997c478bd9Sstevel@tonic-gate 5007c478bd9Sstevel@tonic-gate /* 5017c478bd9Sstevel@tonic-gate * Construct the reply 5027c478bd9Sstevel@tonic-gate */ 5037c478bd9Sstevel@tonic-gate rep->length = 6 + ap_rep.length + cipher.length; 5047c478bd9Sstevel@tonic-gate if ((rep->data = (char *)malloc(rep->length)) == NULL) { 5057c478bd9Sstevel@tonic-gate ret = errno; 5067c478bd9Sstevel@tonic-gate goto bailout; 5077c478bd9Sstevel@tonic-gate } 5087c478bd9Sstevel@tonic-gate ptr = rep->data; 5097c478bd9Sstevel@tonic-gate 5107c478bd9Sstevel@tonic-gate /* 5117c478bd9Sstevel@tonic-gate * Length 5127c478bd9Sstevel@tonic-gate */ 5137c478bd9Sstevel@tonic-gate *ptr++ = (rep->length>>8) & 0xff; 5147c478bd9Sstevel@tonic-gate *ptr++ = rep->length & 0xff; 5157c478bd9Sstevel@tonic-gate 5167c478bd9Sstevel@tonic-gate /* 5177c478bd9Sstevel@tonic-gate * Version == 0x0001 big-endian 5187c478bd9Sstevel@tonic-gate */ 5197c478bd9Sstevel@tonic-gate *ptr++ = 0; 5207c478bd9Sstevel@tonic-gate *ptr++ = 1; 5217c478bd9Sstevel@tonic-gate 5227c478bd9Sstevel@tonic-gate /* 5237c478bd9Sstevel@tonic-gate * ap_rep length, big-endian 5247c478bd9Sstevel@tonic-gate */ 5257c478bd9Sstevel@tonic-gate *ptr++ = (ap_rep.length>>8) & 0xff; 5267c478bd9Sstevel@tonic-gate *ptr++ = ap_rep.length & 0xff; 5277c478bd9Sstevel@tonic-gate 5287c478bd9Sstevel@tonic-gate /* 5297c478bd9Sstevel@tonic-gate * ap-rep data 5307c478bd9Sstevel@tonic-gate */ 5317c478bd9Sstevel@tonic-gate if (ap_rep.length) { 5327c478bd9Sstevel@tonic-gate (void) memcpy(ptr, ap_rep.data, ap_rep.length); 5337c478bd9Sstevel@tonic-gate ptr += ap_rep.length; 5347c478bd9Sstevel@tonic-gate } 5357c478bd9Sstevel@tonic-gate 5367c478bd9Sstevel@tonic-gate /* 5377c478bd9Sstevel@tonic-gate * krb-priv or krb-error 5387c478bd9Sstevel@tonic-gate */ 5397c478bd9Sstevel@tonic-gate (void) memcpy(ptr, cipher.data, cipher.length); 5407c478bd9Sstevel@tonic-gate 5417c478bd9Sstevel@tonic-gate bailout: 5427c478bd9Sstevel@tonic-gate if (auth_context) 5437c478bd9Sstevel@tonic-gate krb5_auth_con_free(context, auth_context); 5447c478bd9Sstevel@tonic-gate if (changepw) 5457c478bd9Sstevel@tonic-gate krb5_free_principal(context, changepw); 5467c478bd9Sstevel@tonic-gate if (ap_rep.data != NULL) 5477c478bd9Sstevel@tonic-gate krb5_xfree(ap_rep.data); 5487c478bd9Sstevel@tonic-gate if (ticket) 5497c478bd9Sstevel@tonic-gate krb5_free_ticket(context, ticket); 5507c478bd9Sstevel@tonic-gate if (clear.data != NULL) 5517c478bd9Sstevel@tonic-gate krb5_xfree(clear.data); 5527c478bd9Sstevel@tonic-gate if (cipher.data != NULL) 5537c478bd9Sstevel@tonic-gate krb5_xfree(cipher.data); 5547c478bd9Sstevel@tonic-gate if (allocated_mem) 5557c478bd9Sstevel@tonic-gate krb5_xfree(local_kaddr.contents); 5567c478bd9Sstevel@tonic-gate 5577c478bd9Sstevel@tonic-gate return (ret); 5587c478bd9Sstevel@tonic-gate } 5597c478bd9Sstevel@tonic-gate 5607c478bd9Sstevel@tonic-gate 5617c478bd9Sstevel@tonic-gate /* 5627c478bd9Sstevel@tonic-gate * This routine is used to handle password-change requests received 5637c478bd9Sstevel@tonic-gate * on kpasswd-port 464 from MIT/M$ clients. 5647c478bd9Sstevel@tonic-gate */ 5657c478bd9Sstevel@tonic-gate void 5667c478bd9Sstevel@tonic-gate handle_chpw(krb5_context context, int s1, 5677c478bd9Sstevel@tonic-gate void *serverhandle, kadm5_config_params *params) 5687c478bd9Sstevel@tonic-gate { 5697c478bd9Sstevel@tonic-gate krb5_error_code ret; 5707c478bd9Sstevel@tonic-gate char req[MAXAPREQ]; 5717c478bd9Sstevel@tonic-gate int len; 5727c478bd9Sstevel@tonic-gate struct sockaddr_in from; 5737c478bd9Sstevel@tonic-gate int fromlen; 5747c478bd9Sstevel@tonic-gate krb5_keytab kt; 5757c478bd9Sstevel@tonic-gate krb5_data reqdata, repdata; 5767c478bd9Sstevel@tonic-gate int s2 = -1; 5777c478bd9Sstevel@tonic-gate 5787c478bd9Sstevel@tonic-gate reqdata.length = 0; 5797c478bd9Sstevel@tonic-gate reqdata.data = NULL; 5807c478bd9Sstevel@tonic-gate repdata.length = 0; 5817c478bd9Sstevel@tonic-gate repdata.data = NULL; 5827c478bd9Sstevel@tonic-gate 5837c478bd9Sstevel@tonic-gate fromlen = sizeof (from); 5847c478bd9Sstevel@tonic-gate 5857c478bd9Sstevel@tonic-gate if ((len = recvfrom(s1, req, sizeof (req), 0, (struct sockaddr *)&from, 5867c478bd9Sstevel@tonic-gate &fromlen)) < 0) { 5877c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't receive " 5887c478bd9Sstevel@tonic-gate "request: %s"), error_message(errno)); 5897c478bd9Sstevel@tonic-gate return; 5907c478bd9Sstevel@tonic-gate } 5917c478bd9Sstevel@tonic-gate 592*46c8d03dSMark Phalan /* 593*46c8d03dSMark Phalan * Solaris Kerberos: 594*46c8d03dSMark Phalan * The only caller is kadmind, which is the master and therefore has the 595*46c8d03dSMark Phalan * correct keys in the KDB, rather than obtaining them via the 596*46c8d03dSMark Phalan * kadm5.keytab, by default. 597*46c8d03dSMark Phalan */ 598*46c8d03dSMark Phalan if ((ret = krb5_kt_resolve(context, "KDB:", &kt))) { 5997c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't open " 6007c478bd9Sstevel@tonic-gate "admin keytab %s"), error_message(ret)); 6017c478bd9Sstevel@tonic-gate return; 6027c478bd9Sstevel@tonic-gate } 6037c478bd9Sstevel@tonic-gate 6047c478bd9Sstevel@tonic-gate reqdata.length = len; 6057c478bd9Sstevel@tonic-gate reqdata.data = req; 6067c478bd9Sstevel@tonic-gate 6077c478bd9Sstevel@tonic-gate /* 6087c478bd9Sstevel@tonic-gate * This is really obscure. s1 is used for all communications. it 6097c478bd9Sstevel@tonic-gate * is left unconnected in case the server is multihomed and routes 6107c478bd9Sstevel@tonic-gate * are asymmetric. s2 is connected to resolve routes and get 6117c478bd9Sstevel@tonic-gate * addresses. this is the *only* way to get proper addresses for 6127c478bd9Sstevel@tonic-gate * multihomed hosts if routing is asymmetric. 6137c478bd9Sstevel@tonic-gate * 6147c478bd9Sstevel@tonic-gate * A related problem in the server, but not the client, is that 6157c478bd9Sstevel@tonic-gate * many os's have no way to disconnect a connected udp socket, so 6167c478bd9Sstevel@tonic-gate * the s2 socket needs to be closed and recreated for each 6177c478bd9Sstevel@tonic-gate * request. The s1 socket must not be closed, or else queued 6187c478bd9Sstevel@tonic-gate * requests will be lost. 6197c478bd9Sstevel@tonic-gate * 6207c478bd9Sstevel@tonic-gate * A "naive" client implementation (one socket, no connect, 6217c478bd9Sstevel@tonic-gate * hostname resolution to get the local ip addr) will work and 6227c478bd9Sstevel@tonic-gate * interoperate if the client is single-homed. 6237c478bd9Sstevel@tonic-gate */ 6247c478bd9Sstevel@tonic-gate 6257c478bd9Sstevel@tonic-gate if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) < 0) { 6267c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Cannot create " 6277c478bd9Sstevel@tonic-gate "connecting socket: %s"), error_message(errno)); 6287c478bd9Sstevel@tonic-gate goto cleanup; 6297c478bd9Sstevel@tonic-gate } 6307c478bd9Sstevel@tonic-gate 6317c478bd9Sstevel@tonic-gate if (connect(s2, (struct sockaddr *)&from, sizeof (from)) < 0) { 6327c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Couldn't connect " 6337c478bd9Sstevel@tonic-gate "to client: %s"), error_message(errno)); 6347c478bd9Sstevel@tonic-gate if (s2 > 0) 6357c478bd9Sstevel@tonic-gate (void) close(s2); 6367c478bd9Sstevel@tonic-gate goto cleanup; 6377c478bd9Sstevel@tonic-gate } 6387c478bd9Sstevel@tonic-gate 6397c478bd9Sstevel@tonic-gate if ((ret = process_chpw_request(context, serverhandle, 6407c478bd9Sstevel@tonic-gate params->realm, s2, kt, &from, 6417c478bd9Sstevel@tonic-gate &reqdata, &repdata))) { 6427c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Error processing " 6437c478bd9Sstevel@tonic-gate "request: %s"), error_message(ret)); 6447c478bd9Sstevel@tonic-gate } 6457c478bd9Sstevel@tonic-gate 6467c478bd9Sstevel@tonic-gate if (s2 > 0) 6477c478bd9Sstevel@tonic-gate (void) close(s2); 6487c478bd9Sstevel@tonic-gate 6497c478bd9Sstevel@tonic-gate if (repdata.length == 0 || repdata.data == NULL) { 6507c478bd9Sstevel@tonic-gate /* 6517c478bd9Sstevel@tonic-gate * Just return. This means something really bad happened 6527c478bd9Sstevel@tonic-gate */ 6537c478bd9Sstevel@tonic-gate goto cleanup; 6547c478bd9Sstevel@tonic-gate } 6557c478bd9Sstevel@tonic-gate 6567c478bd9Sstevel@tonic-gate len = sendto(s1, repdata.data, repdata.length, 0, 6577c478bd9Sstevel@tonic-gate (struct sockaddr *)&from, sizeof (from)); 6587c478bd9Sstevel@tonic-gate 6597c478bd9Sstevel@tonic-gate if (len < repdata.length) { 6607c478bd9Sstevel@tonic-gate krb5_xfree(repdata.data); 6617c478bd9Sstevel@tonic-gate 6627c478bd9Sstevel@tonic-gate krb5_klog_syslog(LOG_ERR, gettext("chpw: Error sending reply:" 6637c478bd9Sstevel@tonic-gate " %s"), error_message(errno)); 6647c478bd9Sstevel@tonic-gate goto cleanup; 6657c478bd9Sstevel@tonic-gate } 6667c478bd9Sstevel@tonic-gate 6677c478bd9Sstevel@tonic-gate if (repdata.data != NULL) 6687c478bd9Sstevel@tonic-gate krb5_xfree(repdata.data); 6697c478bd9Sstevel@tonic-gate cleanup: 6707c478bd9Sstevel@tonic-gate krb5_kt_close(context, kt); 6717c478bd9Sstevel@tonic-gate } 672