 2c714acde1
			
		
	
	2c714acde1
	
	
	
		
			
			git-svn-id: svn://svn.h5l.se/heimdal/trunk/heimdal@16046 ec53bebd-3082-4978-b11e-865c3cabbd6b
		
			
				
	
	
		
			817 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			817 lines
		
	
	
		
			19 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
| /*
 | |
|  * Copyright (c) 1997 - 2005 Kungliga Tekniska Högskolan
 | |
|  * (Royal Institute of Technology, Stockholm, Sweden). 
 | |
|  * All rights reserved. 
 | |
|  *
 | |
|  * Redistribution and use in source and binary forms, with or without 
 | |
|  * modification, are permitted provided that the following conditions 
 | |
|  * are met: 
 | |
|  *
 | |
|  * 1. Redistributions of source code must retain the above copyright 
 | |
|  *    notice, this list of conditions and the following disclaimer. 
 | |
|  *
 | |
|  * 2. Redistributions in binary form must reproduce the above copyright 
 | |
|  *    notice, this list of conditions and the following disclaimer in the 
 | |
|  *    documentation and/or other materials provided with the distribution. 
 | |
|  *
 | |
|  * 3. Neither the name of the Institute nor the names of its contributors 
 | |
|  *    may be used to endorse or promote products derived from this software 
 | |
|  *    without specific prior written permission. 
 | |
|  *
 | |
|  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
 | |
|  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
 | |
|  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
 | |
|  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
 | |
|  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
 | |
|  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
 | |
|  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
 | |
|  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
 | |
|  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
 | |
|  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
 | |
|  * SUCH DAMAGE. 
 | |
|  */
 | |
| 
 | |
| #include <krb5_locl.h>
 | |
| 
 | |
| RCSID("$Id$");
 | |
| 
 | |
| static void
 | |
| str2data (krb5_data *d,
 | |
| 	  const char *fmt,
 | |
| 	  ...) __attribute__ ((format (printf, 2, 3)));
 | |
| 
 | |
| static void
 | |
| str2data (krb5_data *d,
 | |
| 	  const char *fmt,
 | |
| 	  ...)
 | |
| {
 | |
|     va_list args;
 | |
| 
 | |
|     va_start(args, fmt);
 | |
|     d->length = vasprintf ((char **)&d->data, fmt, args);
 | |
|     va_end(args);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Change password protocol defined by
 | |
|  * draft-ietf-cat-kerb-chg-password-02.txt
 | |
|  * 
 | |
|  * Share the response part of the protocol with MS set password
 | |
|  * (RFC3244)
 | |
|  */
 | |
| 
 | |
| static krb5_error_code
 | |
| chgpw_send_request (krb5_context context,
 | |
| 		    krb5_auth_context *auth_context,
 | |
| 		    krb5_creds *creds,
 | |
| 		    krb5_principal targprinc,
 | |
| 		    int is_stream,
 | |
| 		    int sock,
 | |
| 		    const char *passwd,
 | |
| 		    const char *host)
 | |
| {
 | |
|     krb5_error_code ret;
 | |
|     krb5_data ap_req_data;
 | |
|     krb5_data krb_priv_data;
 | |
|     krb5_data passwd_data;
 | |
|     size_t len;
 | |
|     u_char header[6];
 | |
|     u_char *p;
 | |
|     struct iovec iov[3];
 | |
|     struct msghdr msghdr;
 | |
| 
 | |
|     if (is_stream)
 | |
| 	return KRB5_KPASSWD_MALFORMED;
 | |
| 
 | |
|     if (targprinc &&
 | |
| 	krb5_principal_compare(context, creds->client, targprinc) != TRUE)
 | |
| 	return KRB5_KPASSWD_MALFORMED;
 | |
| 
 | |
|     krb5_data_zero (&ap_req_data);
 | |
| 
 | |
|     ret = krb5_mk_req_extended (context,
 | |
| 				auth_context,
 | |
| 				AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
 | |
| 				NULL, /* in_data */
 | |
| 				creds,
 | |
| 				&ap_req_data);
 | |
|     if (ret)
 | |
| 	return ret;
 | |
| 
 | |
|     passwd_data.data   = rk_UNCONST(passwd);
 | |
|     passwd_data.length = strlen(passwd);
 | |
| 
 | |
|     krb5_data_zero (&krb_priv_data);
 | |
| 
 | |
|     ret = krb5_mk_priv (context,
 | |
| 			*auth_context,
 | |
| 			&passwd_data,
 | |
| 			&krb_priv_data,
 | |
| 			NULL);
 | |
|     if (ret)
 | |
| 	goto out2;
 | |
| 
 | |
|     len = 6 + ap_req_data.length + krb_priv_data.length;
 | |
|     p = header;
 | |
|     *p++ = (len >> 8) & 0xFF;
 | |
|     *p++ = (len >> 0) & 0xFF;
 | |
|     *p++ = 0;
 | |
|     *p++ = 1;
 | |
|     *p++ = (ap_req_data.length >> 8) & 0xFF;
 | |
|     *p++ = (ap_req_data.length >> 0) & 0xFF;
 | |
| 
 | |
|     memset(&msghdr, 0, sizeof(msghdr));
 | |
|     msghdr.msg_name       = NULL;
 | |
|     msghdr.msg_namelen    = 0;
 | |
|     msghdr.msg_iov        = iov;
 | |
|     msghdr.msg_iovlen     = sizeof(iov)/sizeof(*iov);
 | |
| #if 0
 | |
|     msghdr.msg_control    = NULL;
 | |
|     msghdr.msg_controllen = 0;
 | |
| #endif
 | |
| 
 | |
|     iov[0].iov_base    = (void*)header;
 | |
|     iov[0].iov_len     = 6;
 | |
|     iov[1].iov_base    = ap_req_data.data;
 | |
|     iov[1].iov_len     = ap_req_data.length;
 | |
|     iov[2].iov_base    = krb_priv_data.data;
 | |
|     iov[2].iov_len     = krb_priv_data.length;
 | |
| 
 | |
|     if (sendmsg (sock, &msghdr, 0) < 0) {
 | |
| 	ret = errno;
 | |
| 	krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret));
 | |
|     }
 | |
| 
 | |
|     krb5_data_free (&krb_priv_data);
 | |
| out2:
 | |
|     krb5_data_free (&ap_req_data);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  * Set password protocol as defined by RFC3244 --
 | |
|  * Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols
 | |
|  */
 | |
| 
 | |
| static krb5_error_code
 | |
| setpw_send_request (krb5_context context,
 | |
| 		    krb5_auth_context *auth_context,
 | |
| 		    krb5_creds *creds,
 | |
| 		    krb5_principal targprinc,
 | |
| 		    int is_stream,
 | |
| 		    int sock,
 | |
| 		    const char *passwd,
 | |
| 		    const char *host)
 | |
| {
 | |
|     krb5_error_code ret;
 | |
|     krb5_data ap_req_data;
 | |
|     krb5_data krb_priv_data;
 | |
|     krb5_data pwd_data;
 | |
|     ChangePasswdDataMS chpw;
 | |
|     size_t len;
 | |
|     u_char header[4 + 6];
 | |
|     u_char *p;
 | |
|     struct iovec iov[3];
 | |
|     struct msghdr msghdr;
 | |
| 
 | |
|     krb5_data_zero (&ap_req_data);
 | |
| 
 | |
|     ret = krb5_mk_req_extended (context,
 | |
| 				auth_context,
 | |
| 				AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
 | |
| 				NULL, /* in_data */
 | |
| 				creds,
 | |
| 				&ap_req_data);
 | |
|     if (ret)
 | |
| 	return ret;
 | |
| 
 | |
|     chpw.newpasswd.length = strlen(passwd);
 | |
|     chpw.newpasswd.data = rk_UNCONST(passwd);
 | |
|     if (targprinc) {
 | |
| 	chpw.targname = &targprinc->name;
 | |
| 	chpw.targrealm = &targprinc->realm;
 | |
|     } else {
 | |
| 	chpw.targname = NULL;
 | |
| 	chpw.targrealm = NULL;
 | |
|     }
 | |
| 	
 | |
|     ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length,
 | |
| 		       &chpw, &len, ret);
 | |
|     if (ret) {
 | |
| 	krb5_data_free (&ap_req_data);
 | |
| 	return ret;
 | |
|     }
 | |
| 
 | |
|     if(pwd_data.length != len)
 | |
| 	krb5_abortx(context, "internal error in ASN.1 encoder");
 | |
| 
 | |
|     ret = krb5_mk_priv (context,
 | |
| 			*auth_context,
 | |
| 			&pwd_data,
 | |
| 			&krb_priv_data,
 | |
| 			NULL);
 | |
|     if (ret)
 | |
| 	goto out2;
 | |
| 
 | |
|     len = 6 + ap_req_data.length + krb_priv_data.length;
 | |
|     p = header;
 | |
|     if (is_stream) {
 | |
| 	_krb5_put_int(p, len, 4);
 | |
| 	p += 4;
 | |
|     }
 | |
|     *p++ = (len >> 8) & 0xFF;
 | |
|     *p++ = (len >> 0) & 0xFF;
 | |
|     *p++ = 0xff;
 | |
|     *p++ = 0x80;
 | |
|     *p++ = (ap_req_data.length >> 8) & 0xFF;
 | |
|     *p++ = (ap_req_data.length >> 0) & 0xFF;
 | |
| 
 | |
|     memset(&msghdr, 0, sizeof(msghdr));
 | |
|     msghdr.msg_name       = NULL;
 | |
|     msghdr.msg_namelen    = 0;
 | |
|     msghdr.msg_iov        = iov;
 | |
|     msghdr.msg_iovlen     = sizeof(iov)/sizeof(*iov);
 | |
| #if 0
 | |
|     msghdr.msg_control    = NULL;
 | |
|     msghdr.msg_controllen = 0;
 | |
| #endif
 | |
| 
 | |
|     iov[0].iov_base    = (void*)header;
 | |
|     if (is_stream)
 | |
| 	iov[0].iov_len     = 10;
 | |
|     else
 | |
| 	iov[0].iov_len     = 6;
 | |
|     iov[1].iov_base    = ap_req_data.data;
 | |
|     iov[1].iov_len     = ap_req_data.length;
 | |
|     iov[2].iov_base    = krb_priv_data.data;
 | |
|     iov[2].iov_len     = krb_priv_data.length;
 | |
| 
 | |
|     if (sendmsg (sock, &msghdr, 0) < 0) {
 | |
| 	ret = errno;
 | |
| 	krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret));
 | |
|     }
 | |
| 
 | |
|     krb5_data_free (&krb_priv_data);
 | |
| out2:
 | |
|     krb5_data_free (&ap_req_data);
 | |
|     krb5_data_free (&pwd_data);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| static krb5_error_code
 | |
| process_reply (krb5_context context,
 | |
| 	       krb5_auth_context auth_context,
 | |
| 	       int is_stream,
 | |
| 	       int sock,
 | |
| 	       int *result_code,
 | |
| 	       krb5_data *result_code_string,
 | |
| 	       krb5_data *result_string,
 | |
| 	       const char *host)
 | |
| {
 | |
|     krb5_error_code ret;
 | |
|     u_char reply[1024 * 3];
 | |
|     ssize_t len;
 | |
|     u_int16_t pkt_len, pkt_ver;
 | |
|     krb5_data ap_rep_data;
 | |
|     int save_errno;
 | |
| 
 | |
|     len = 0;
 | |
|     if (is_stream) {
 | |
| 	while (len < sizeof(reply)) {
 | |
| 	    unsigned long size;
 | |
| 
 | |
| 	    ret = recvfrom (sock, reply + len, sizeof(reply) - len, 
 | |
| 			    0, NULL, NULL);
 | |
| 	    if (ret < 0) {
 | |
| 		save_errno = errno;
 | |
| 		krb5_set_error_string(context, "recvfrom %s: %s",
 | |
| 				      host, strerror(save_errno));
 | |
| 		return save_errno;
 | |
| 	    } else if (ret == 0) {
 | |
| 		krb5_set_error_string(context, "recvfrom timeout %s", host);
 | |
| 		return 1;
 | |
| 	    }
 | |
| 	    len += ret;
 | |
| 	    if (len < 4)
 | |
| 		continue;
 | |
| 	    _krb5_get_int(reply, &size, 4);
 | |
| 	    if (size + 4 < len)
 | |
| 		continue;
 | |
| 	    memmove(reply, reply + 4, size);		
 | |
| 	    len = size;
 | |
| 	    break;
 | |
| 	}
 | |
| 	if (len == sizeof(reply)) {
 | |
| 	    krb5_set_error_string(context, "message too large from %s",
 | |
| 				  host);
 | |
| 	    return ENOMEM;
 | |
| 	}
 | |
|     } else {
 | |
| 	ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL);
 | |
| 	if (ret < 0) {
 | |
| 	    save_errno = errno;
 | |
| 	    krb5_set_error_string(context, "recvfrom %s: %s",
 | |
| 				  host, strerror(save_errno));
 | |
| 	    return save_errno;
 | |
| 	}
 | |
| 	len = ret;
 | |
|     }
 | |
| 
 | |
|     if (len < 6) {
 | |
| 	str2data (result_string, "server %s sent to too short message "
 | |
| 		  "(%ld bytes)", host, (long)len);
 | |
| 	*result_code = KRB5_KPASSWD_MALFORMED;
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     pkt_len = (reply[0] << 8) | (reply[1]);
 | |
|     pkt_ver = (reply[2] << 8) | (reply[3]);
 | |
| 
 | |
|     if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) {
 | |
| 	KRB_ERROR error;
 | |
| 	size_t size;
 | |
| 	u_char *p;
 | |
| 
 | |
| 	memset(&error, 0, sizeof(error));
 | |
| 
 | |
| 	ret = decode_KRB_ERROR(reply, len, &error, &size);
 | |
| 	if (ret)
 | |
| 	    return ret;
 | |
| 
 | |
| 	if (error.e_data->length < 2) {
 | |
| 	    str2data(result_string, "server %s sent too short "
 | |
| 		     "e_data to print anything usable", host);
 | |
| 	    free_KRB_ERROR(&error);
 | |
| 	    *result_code = KRB5_KPASSWD_MALFORMED;
 | |
| 	    return 0;
 | |
| 	}
 | |
| 
 | |
| 	p = error.e_data->data;
 | |
| 	*result_code = (p[0] << 8) | p[1];
 | |
| 	if (error.e_data->length == 2)
 | |
| 	    str2data(result_string, "server only sent error code");
 | |
| 	else 
 | |
| 	    krb5_data_copy (result_string,
 | |
| 			    p + 2,
 | |
| 			    error.e_data->length - 2);
 | |
| 	free_KRB_ERROR(&error);
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     if (pkt_len != len) {
 | |
| 	str2data (result_string, "client: wrong len in reply");
 | |
| 	*result_code = KRB5_KPASSWD_MALFORMED;
 | |
| 	return 0;
 | |
|     }
 | |
|     if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) {
 | |
| 	str2data (result_string,
 | |
| 		  "client: wrong version number (%d)", pkt_ver);
 | |
| 	*result_code = KRB5_KPASSWD_MALFORMED;
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     ap_rep_data.data = reply + 6;
 | |
|     ap_rep_data.length  = (reply[4] << 8) | (reply[5]);
 | |
|   
 | |
|     if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) {
 | |
| 	str2data (result_string, "client: wrong AP len in reply");
 | |
| 	*result_code = KRB5_KPASSWD_MALFORMED;
 | |
| 	return 0;
 | |
|     }
 | |
| 
 | |
|     if (ap_rep_data.length) {
 | |
| 	krb5_ap_rep_enc_part *ap_rep;
 | |
| 	krb5_data priv_data;
 | |
| 	u_char *p;
 | |
| 
 | |
| 	priv_data.data   = (u_char*)ap_rep_data.data + ap_rep_data.length;
 | |
| 	priv_data.length = len - ap_rep_data.length - 6;
 | |
| 
 | |
| 	ret = krb5_rd_rep (context,
 | |
| 			   auth_context,
 | |
| 			   &ap_rep_data,
 | |
| 			   &ap_rep);
 | |
| 	if (ret)
 | |
| 	    return ret;
 | |
| 
 | |
| 	krb5_free_ap_rep_enc_part (context, ap_rep);
 | |
| 
 | |
| 	ret = krb5_rd_priv (context,
 | |
| 			    auth_context,
 | |
| 			    &priv_data,
 | |
| 			    result_code_string,
 | |
| 			    NULL);
 | |
| 	if (ret) {
 | |
| 	    krb5_data_free (result_code_string);
 | |
| 	    return ret;
 | |
| 	}
 | |
| 
 | |
| 	if (result_code_string->length < 2) {
 | |
| 	    *result_code = KRB5_KPASSWD_MALFORMED;
 | |
| 	    str2data (result_string,
 | |
| 		      "client: bad length in result");
 | |
| 	    return 0;
 | |
| 	}
 | |
| 
 | |
|         p = result_code_string->data;
 | |
|       
 | |
|         *result_code = (p[0] << 8) | p[1];
 | |
|         krb5_data_copy (result_string,
 | |
|                         (unsigned char*)result_code_string->data + 2,
 | |
|                         result_code_string->length - 2);
 | |
|         return 0;
 | |
|     } else {
 | |
| 	KRB_ERROR error;
 | |
| 	size_t size;
 | |
| 	u_char *p;
 | |
|       
 | |
| 	ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size);
 | |
| 	if (ret) {
 | |
| 	    return ret;
 | |
| 	}
 | |
| 	if (error.e_data->length < 2) {
 | |
| 	    krb5_warnx (context, "too short e_data to print anything usable");
 | |
| 	    return 1;		/* XXX */
 | |
| 	}
 | |
| 
 | |
| 	p = error.e_data->data;
 | |
| 	*result_code = (p[0] << 8) | p[1];
 | |
| 	krb5_data_copy (result_string,
 | |
| 			p + 2,
 | |
| 			error.e_data->length - 2);
 | |
| 	return 0;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * change the password using the credentials in `creds' (for the
 | |
|  * principal indicated in them) to `newpw', storing the result of
 | |
|  * the operation in `result_*' and an error code or 0.
 | |
|  */
 | |
| 
 | |
| typedef krb5_error_code (*kpwd_send_request) (krb5_context,
 | |
| 					      krb5_auth_context *,
 | |
| 					      krb5_creds *,
 | |
| 					      krb5_principal,
 | |
| 					      int,
 | |
| 					      int,
 | |
| 					      const char *,
 | |
| 					      const char *);
 | |
| typedef krb5_error_code (*kpwd_process_reply) (krb5_context,
 | |
| 					       krb5_auth_context,
 | |
| 					       int,
 | |
| 					       int,
 | |
| 					       int *,
 | |
| 					       krb5_data *,
 | |
| 					       krb5_data *,
 | |
| 					       const char *);
 | |
| 
 | |
| static struct kpwd_proc {
 | |
|     const char *name;
 | |
|     int flags;
 | |
| #define SUPPORT_TCP	1
 | |
| #define SUPPORT_UDP	2
 | |
|     kpwd_send_request send_req;
 | |
|     kpwd_process_reply process_rep;
 | |
| } procs[] = {
 | |
|     {
 | |
| 	"MS set password", 
 | |
| 	SUPPORT_TCP|SUPPORT_UDP,
 | |
| 	setpw_send_request, 
 | |
| 	process_reply
 | |
|     },
 | |
|     {
 | |
| 	"change password",
 | |
| 	SUPPORT_UDP,
 | |
| 	chgpw_send_request,
 | |
| 	process_reply
 | |
|     },
 | |
|     { NULL }
 | |
| };
 | |
| 
 | |
| static struct kpwd_proc *
 | |
| find_chpw_proto(const char *name)
 | |
| {
 | |
|     struct kpwd_proc *p;
 | |
|     for (p = procs; p->name != NULL; p++) {
 | |
| 	if (strcmp(p->name, name) == 0)
 | |
| 	    return p;
 | |
|     }
 | |
|     return NULL;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  */
 | |
| 
 | |
| static krb5_error_code
 | |
| change_password_loop (krb5_context	context,
 | |
| 		      krb5_creds	*creds,
 | |
| 		      krb5_principal	targprinc,
 | |
| 		      const char	*newpw,
 | |
| 		      int		*result_code,
 | |
| 		      krb5_data		*result_code_string,
 | |
| 		      krb5_data		*result_string,
 | |
| 		      struct kpwd_proc	*proc)
 | |
| {
 | |
|     krb5_error_code ret;
 | |
|     krb5_auth_context auth_context = NULL;
 | |
|     krb5_krbhst_handle handle = NULL;
 | |
|     krb5_krbhst_info *hi;
 | |
|     int sock;
 | |
|     int i;
 | |
|     int done = 0;
 | |
|     krb5_realm realm = creds->client->realm;
 | |
| 
 | |
|     ret = krb5_auth_con_init (context, &auth_context);
 | |
|     if (ret)
 | |
| 	return ret;
 | |
| 
 | |
|     krb5_auth_con_setflags (context, auth_context,
 | |
| 			    KRB5_AUTH_CONTEXT_DO_SEQUENCE);
 | |
| 
 | |
|     ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
| 
 | |
|     while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) {
 | |
| 	struct addrinfo *ai, *a;
 | |
| 	int is_stream;
 | |
| 
 | |
| 	switch (hi->proto) {
 | |
| 	case KRB5_KRBHST_UDP:
 | |
| 	    if ((proc->flags & SUPPORT_UDP) == 0)
 | |
| 		continue;
 | |
| 	    is_stream = 0;
 | |
| 	    break;
 | |
| 	case KRB5_KRBHST_TCP:
 | |
| 	    if ((proc->flags & SUPPORT_TCP) == 0)
 | |
| 		continue;
 | |
| 	    is_stream = 1;
 | |
| 	    break;
 | |
| 	default:
 | |
| 	    continue;
 | |
| 	}
 | |
| 
 | |
| 	ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
 | |
| 	if (ret)
 | |
| 	    continue;
 | |
| 
 | |
| 	for (a = ai; !done && a != NULL; a = a->ai_next) {
 | |
| 	    int replied = 0;
 | |
| 
 | |
| 	    sock = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
 | |
| 	    if (sock < 0)
 | |
| 		continue;
 | |
| 
 | |
| 	    ret = connect(sock, a->ai_addr, a->ai_addrlen);
 | |
| 	    if (ret < 0) {
 | |
| 		close (sock);
 | |
| 		goto out;
 | |
| 	    }
 | |
| 
 | |
| 	    ret = krb5_auth_con_genaddrs (context, auth_context, sock,
 | |
| 					  KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR);
 | |
| 	    if (ret) {
 | |
| 		close (sock);
 | |
| 		goto out;
 | |
| 	    }
 | |
| 
 | |
| 	    for (i = 0; !done && i < 5; ++i) {
 | |
| 		fd_set fdset;
 | |
| 		struct timeval tv;
 | |
| 
 | |
| 		if (!replied) {
 | |
| 		    replied = 0;
 | |
| 		    
 | |
| 		    ret = (*proc->send_req) (context,
 | |
| 					     &auth_context,
 | |
| 					     creds,
 | |
| 					     targprinc,
 | |
| 					     is_stream,
 | |
| 					     sock,
 | |
| 					     newpw,
 | |
| 					     hi->hostname);
 | |
| 		    if (ret) {
 | |
| 			close(sock);
 | |
| 			goto out;
 | |
| 		    }
 | |
| 		}
 | |
| 	    
 | |
| 		if (sock >= FD_SETSIZE) {
 | |
| 		    krb5_set_error_string(context, "fd %d too large", sock);
 | |
| 		    ret = ERANGE;
 | |
| 		    close (sock);
 | |
| 		    goto out;
 | |
| 		}
 | |
| 
 | |
| 		FD_ZERO(&fdset);
 | |
| 		FD_SET(sock, &fdset);
 | |
| 		tv.tv_usec = 0;
 | |
| 		tv.tv_sec  = 1 + (1 << i);
 | |
| 
 | |
| 		ret = select (sock + 1, &fdset, NULL, NULL, &tv);
 | |
| 		if (ret < 0 && errno != EINTR) {
 | |
| 		    close(sock);
 | |
| 		    goto out;
 | |
| 		}
 | |
| 		if (ret == 1) {
 | |
| 		    ret = (*proc->process_rep) (context,
 | |
| 						auth_context,
 | |
| 						is_stream,
 | |
| 						sock,
 | |
| 						result_code,
 | |
| 						result_code_string,
 | |
| 						result_string,
 | |
| 						hi->hostname);
 | |
| 		    if (ret == 0)
 | |
| 			done = 1;
 | |
| 		    else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL)
 | |
| 			replied = 1;
 | |
| 		} else {
 | |
| 		    ret = KRB5_KDC_UNREACH;
 | |
| 		}
 | |
| 	    }
 | |
| 	    close (sock);
 | |
| 	}
 | |
|     }
 | |
| 
 | |
|  out:
 | |
|     krb5_krbhst_free (context, handle);
 | |
|     krb5_auth_con_free (context, auth_context);
 | |
|     if (done)
 | |
| 	return 0;
 | |
|     else {
 | |
| 	if (ret == KRB5_KDC_UNREACH) {
 | |
| 	    krb5_set_error_string(context,
 | |
| 				  "unable to reach any changepw server "
 | |
| 				  " in realm %s", realm);
 | |
| 	    *result_code = KRB5_KPASSWD_HARDERROR;
 | |
| 	}
 | |
| 	return ret;
 | |
|     }
 | |
| }
 | |
| 
 | |
| 
 | |
| /*
 | |
|  * change the password using the credentials in `creds' (for the
 | |
|  * principal indicated in them) to `newpw', storing the result of
 | |
|  * the operation in `result_*' and an error code or 0.
 | |
|  */
 | |
| 
 | |
| krb5_error_code KRB5_LIB_FUNCTION
 | |
| krb5_change_password (krb5_context	context,
 | |
| 		      krb5_creds	*creds,
 | |
| 		      const char	*newpw,
 | |
| 		      int		*result_code,
 | |
| 		      krb5_data		*result_code_string,
 | |
| 		      krb5_data		*result_string)
 | |
| {
 | |
|     struct kpwd_proc *p = find_chpw_proto("change password");
 | |
| 
 | |
|     *result_code = KRB5_KPASSWD_MALFORMED;
 | |
|     result_code_string->data = result_string->data = NULL;
 | |
|     result_code_string->length = result_string->length = 0;
 | |
| 
 | |
|     if (p == NULL)
 | |
| 	return KRB5_KPASSWD_MALFORMED;
 | |
| 
 | |
|     return change_password_loop(context, creds, NULL, newpw, 
 | |
| 				result_code, result_code_string, 
 | |
| 				result_string, p);
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  */
 | |
| 
 | |
| krb5_error_code KRB5_LIB_FUNCTION
 | |
| krb5_set_password(krb5_context context,
 | |
| 		  krb5_creds *creds,
 | |
| 		  const char *newpw,
 | |
| 		  krb5_principal targprinc,
 | |
| 		  int *result_code,
 | |
| 		  krb5_data *result_code_string,
 | |
| 		  krb5_data *result_string)
 | |
| {
 | |
|     krb5_principal principal = NULL;
 | |
|     krb5_error_code ret = 0;
 | |
|     int i;
 | |
| 
 | |
|     *result_code = KRB5_KPASSWD_MALFORMED;
 | |
|     result_code_string->data = result_string->data = NULL;
 | |
|     result_code_string->length = result_string->length = 0;
 | |
| 
 | |
|     if (targprinc == NULL) {
 | |
| 	ret = krb5_get_default_principal(context, &principal);
 | |
| 	if (ret)
 | |
| 	    return ret;
 | |
|     } else
 | |
| 	principal = targprinc;
 | |
| 
 | |
|     for (i = 0; procs[i].name != NULL; i++) {
 | |
| 	*result_code = 0;
 | |
| 	ret = change_password_loop(context, creds, targprinc, newpw, 
 | |
| 				   result_code, result_code_string, 
 | |
| 				   result_string, 
 | |
| 				   &procs[i]);
 | |
| 	if (ret == 0 && *result_code == 0)
 | |
| 	    break;
 | |
|     }
 | |
| 
 | |
|     if (targprinc == NULL)
 | |
| 	krb5_free_principal(context, principal);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  */
 | |
| 
 | |
| krb5_error_code KRB5_LIB_FUNCTION
 | |
| krb5_set_password_using_ccache(krb5_context context,
 | |
| 			       krb5_ccache ccache,
 | |
| 			       const char *newpw,
 | |
| 			       krb5_principal targprinc,
 | |
| 			       int *result_code,
 | |
| 			       krb5_data *result_code_string,
 | |
| 			       krb5_data *result_string)
 | |
| {
 | |
|     krb5_creds creds, *credsp;
 | |
|     krb5_error_code ret;
 | |
|     krb5_principal principal = NULL;
 | |
| 
 | |
|     *result_code = KRB5_KPASSWD_MALFORMED;
 | |
|     result_code_string->data = result_string->data = NULL;
 | |
|     result_code_string->length = result_string->length = 0;
 | |
| 
 | |
|     memset(&creds, 0, sizeof(creds));
 | |
| 
 | |
|     if (targprinc == NULL) {
 | |
| 	ret = krb5_cc_get_principal(context, ccache, &principal);
 | |
| 	if (ret)
 | |
| 	    return ret;
 | |
|     } else
 | |
| 	principal = targprinc;
 | |
| 
 | |
|     ret = krb5_make_principal(context, &creds.server, 
 | |
| 			      krb5_principal_get_realm(context, principal),
 | |
| 			      "kadmin", "changepw", NULL);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
| 
 | |
|     ret = krb5_cc_get_principal(context, ccache, &creds.client);
 | |
|     if (ret) {
 | |
|         krb5_free_principal(context, creds.server);
 | |
| 	goto out;
 | |
|     }
 | |
| 
 | |
|     ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
 | |
|     krb5_free_principal(context, creds.server);
 | |
|     krb5_free_principal(context, creds.client);
 | |
|     if (ret)
 | |
| 	goto out;
 | |
| 
 | |
|     ret = krb5_set_password(context,
 | |
| 			    credsp,
 | |
| 			    newpw,
 | |
| 			    principal,
 | |
| 			    result_code,
 | |
| 			    result_code_string,
 | |
| 			    result_string);
 | |
| 
 | |
|     krb5_free_creds(context, credsp); 
 | |
| 
 | |
|     return ret;
 | |
|  out:
 | |
|     if (targprinc == NULL)
 | |
| 	krb5_free_principal(context, principal);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| /*
 | |
|  *
 | |
|  */
 | |
| 
 | |
| const char* KRB5_LIB_FUNCTION
 | |
| krb5_passwd_result_to_string (krb5_context context,
 | |
| 			      int result)
 | |
| {
 | |
|     static const char *strings[] = {
 | |
| 	"Success",
 | |
| 	"Malformed",
 | |
| 	"Hard error",
 | |
| 	"Auth error",
 | |
| 	"Soft error" ,
 | |
| 	"Access denied",
 | |
| 	"Bad version",
 | |
| 	"Initial flag needed"
 | |
|     };
 | |
| 
 | |
|     if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)
 | |
| 	return "unknown result code";
 | |
|     else
 | |
| 	return strings[result];
 | |
| }
 |