xref: /freebsd/crypto/heimdal/lib/krb5/changepw.c (revision 1e413cf93298b5b97441a21d9a50fdcd0ee9945e)
1 /*
2  * Copyright (c) 1997 - 2003 Kungliga Tekniska H�gskolan
3  * (Royal Institute of Technology, Stockholm, Sweden).
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  *
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors
18  *    may be used to endorse or promote products derived from this software
19  *    without specific prior written permission.
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
31  * SUCH DAMAGE.
32  */
33 
34 #include <krb5_locl.h>
35 
36 RCSID("$Id: changepw.c,v 1.38.2.1 2004/06/21 08:38:10 lha Exp $");
37 
38 static void
39 str2data (krb5_data *d,
40 	  const char *fmt,
41 	  ...) __attribute__ ((format (printf, 2, 3)));
42 
43 static void
44 str2data (krb5_data *d,
45 	  const char *fmt,
46 	  ...)
47 {
48     va_list args;
49 
50     va_start(args, fmt);
51     d->length = vasprintf ((char **)&d->data, fmt, args);
52     va_end(args);
53 }
54 
55 /*
56  * Change password protocol defined by
57  * draft-ietf-cat-kerb-chg-password-02.txt
58  *
59  * Share the response part of the protocol with MS set password
60  * (RFC3244)
61  */
62 
63 static krb5_error_code
64 chgpw_send_request (krb5_context context,
65 		    krb5_auth_context *auth_context,
66 		    krb5_creds *creds,
67 		    krb5_principal targprinc,
68 		    int is_stream,
69 		    int sock,
70 		    char *passwd,
71 		    const char *host)
72 {
73     krb5_error_code ret;
74     krb5_data ap_req_data;
75     krb5_data krb_priv_data;
76     krb5_data passwd_data;
77     size_t len;
78     u_char header[6];
79     u_char *p;
80     struct iovec iov[3];
81     struct msghdr msghdr;
82 
83     if (is_stream)
84 	return KRB5_KPASSWD_MALFORMED;
85 
86     if (targprinc &&
87 	krb5_principal_compare(context, creds->client, targprinc) != TRUE)
88 	return KRB5_KPASSWD_MALFORMED;
89 
90     krb5_data_zero (&ap_req_data);
91 
92     ret = krb5_mk_req_extended (context,
93 				auth_context,
94 				AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
95 				NULL, /* in_data */
96 				creds,
97 				&ap_req_data);
98     if (ret)
99 	return ret;
100 
101     passwd_data.data   = passwd;
102     passwd_data.length = strlen(passwd);
103 
104     krb5_data_zero (&krb_priv_data);
105 
106     ret = krb5_mk_priv (context,
107 			*auth_context,
108 			&passwd_data,
109 			&krb_priv_data,
110 			NULL);
111     if (ret)
112 	goto out2;
113 
114     len = 6 + ap_req_data.length + krb_priv_data.length;
115     p = header;
116     *p++ = (len >> 8) & 0xFF;
117     *p++ = (len >> 0) & 0xFF;
118     *p++ = 0;
119     *p++ = 1;
120     *p++ = (ap_req_data.length >> 8) & 0xFF;
121     *p++ = (ap_req_data.length >> 0) & 0xFF;
122 
123     memset(&msghdr, 0, sizeof(msghdr));
124     msghdr.msg_name       = NULL;
125     msghdr.msg_namelen    = 0;
126     msghdr.msg_iov        = iov;
127     msghdr.msg_iovlen     = sizeof(iov)/sizeof(*iov);
128 #if 0
129     msghdr.msg_control    = NULL;
130     msghdr.msg_controllen = 0;
131 #endif
132 
133     iov[0].iov_base    = (void*)header;
134     iov[0].iov_len     = 6;
135     iov[1].iov_base    = ap_req_data.data;
136     iov[1].iov_len     = ap_req_data.length;
137     iov[2].iov_base    = krb_priv_data.data;
138     iov[2].iov_len     = krb_priv_data.length;
139 
140     if (sendmsg (sock, &msghdr, 0) < 0) {
141 	ret = errno;
142 	krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret));
143     }
144 
145     krb5_data_free (&krb_priv_data);
146 out2:
147     krb5_data_free (&ap_req_data);
148     return ret;
149 }
150 
151 /*
152  * Set password protocol as defined by RFC3244 --
153  * Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols
154  */
155 
156 static krb5_error_code
157 setpw_send_request (krb5_context context,
158 		    krb5_auth_context *auth_context,
159 		    krb5_creds *creds,
160 		    krb5_principal targprinc,
161 		    int is_stream,
162 		    int sock,
163 		    char *passwd,
164 		    const char *host)
165 {
166     krb5_error_code ret;
167     krb5_data ap_req_data;
168     krb5_data krb_priv_data;
169     krb5_data pwd_data;
170     ChangePasswdDataMS chpw;
171     size_t len;
172     u_char header[4 + 6];
173     u_char *p;
174     struct iovec iov[3];
175     struct msghdr msghdr;
176 
177     krb5_data_zero (&ap_req_data);
178 
179     ret = krb5_mk_req_extended (context,
180 				auth_context,
181 				AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
182 				NULL, /* in_data */
183 				creds,
184 				&ap_req_data);
185     if (ret)
186 	return ret;
187 
188     chpw.newpasswd.length = strlen(passwd);
189     chpw.newpasswd.data = passwd;
190     if (targprinc) {
191 	chpw.targname = &targprinc->name;
192 	chpw.targrealm = &targprinc->realm;
193     } else {
194 	chpw.targname = NULL;
195 	chpw.targrealm = NULL;
196     }
197 
198     ASN1_MALLOC_ENCODE(ChangePasswdDataMS, pwd_data.data, pwd_data.length,
199 		       &chpw, &len, ret);
200     if (ret) {
201 	krb5_data_free (&ap_req_data);
202 	return ret;
203     }
204 
205     if(pwd_data.length != len)
206 	krb5_abortx(context, "internal error in ASN.1 encoder");
207 
208     ret = krb5_mk_priv (context,
209 			*auth_context,
210 			&pwd_data,
211 			&krb_priv_data,
212 			NULL);
213     if (ret)
214 	goto out2;
215 
216     len = 6 + ap_req_data.length + krb_priv_data.length;
217     p = header;
218     if (is_stream) {
219 	_krb5_put_int(p, len, 4);
220 	p += 4;
221     }
222     *p++ = (len >> 8) & 0xFF;
223     *p++ = (len >> 0) & 0xFF;
224     *p++ = 0xff;
225     *p++ = 0x80;
226     *p++ = (ap_req_data.length >> 8) & 0xFF;
227     *p++ = (ap_req_data.length >> 0) & 0xFF;
228 
229     memset(&msghdr, 0, sizeof(msghdr));
230     msghdr.msg_name       = NULL;
231     msghdr.msg_namelen    = 0;
232     msghdr.msg_iov        = iov;
233     msghdr.msg_iovlen     = sizeof(iov)/sizeof(*iov);
234 #if 0
235     msghdr.msg_control    = NULL;
236     msghdr.msg_controllen = 0;
237 #endif
238 
239     iov[0].iov_base    = (void*)header;
240     if (is_stream)
241 	iov[0].iov_len     = 10;
242     else
243 	iov[0].iov_len     = 6;
244     iov[1].iov_base    = ap_req_data.data;
245     iov[1].iov_len     = ap_req_data.length;
246     iov[2].iov_base    = krb_priv_data.data;
247     iov[2].iov_len     = krb_priv_data.length;
248 
249     if (sendmsg (sock, &msghdr, 0) < 0) {
250 	ret = errno;
251 	krb5_set_error_string(context, "sendmsg %s: %s", host, strerror(ret));
252     }
253 
254     krb5_data_free (&krb_priv_data);
255 out2:
256     krb5_data_free (&ap_req_data);
257     krb5_data_free (&pwd_data);
258     return ret;
259 }
260 
261 static krb5_error_code
262 process_reply (krb5_context context,
263 	       krb5_auth_context auth_context,
264 	       int is_stream,
265 	       int sock,
266 	       int *result_code,
267 	       krb5_data *result_code_string,
268 	       krb5_data *result_string,
269 	       const char *host)
270 {
271     krb5_error_code ret;
272     u_char reply[1024 * 3];
273     ssize_t len;
274     u_int16_t pkt_len, pkt_ver;
275     krb5_data ap_rep_data;
276     int save_errno;
277 
278     len = 0;
279     if (is_stream) {
280 	while (len < sizeof(reply)) {
281 	    unsigned long size;
282 
283 	    ret = recvfrom (sock, reply + len, sizeof(reply) - len,
284 			    0, NULL, NULL);
285 	    if (ret < 0) {
286 		save_errno = errno;
287 		krb5_set_error_string(context, "recvfrom %s: %s",
288 				      host, strerror(save_errno));
289 		return save_errno;
290 	    } else if (ret == 0) {
291 		krb5_set_error_string(context, "recvfrom timeout %s", host);
292 		return 1;
293 	    }
294 	    len += ret;
295 	    if (len < 4)
296 		continue;
297 	    _krb5_get_int(reply, &size, 4);
298 	    if (size + 4 < len)
299 		continue;
300 	    memmove(reply, reply + 4, size);
301 	    len = size;
302 	    break;
303 	}
304 	if (len == sizeof(reply)) {
305 	    krb5_set_error_string(context, "message too large from %s",
306 				  host);
307 	    return ENOMEM;
308 	}
309     } else {
310 	ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL);
311 	if (ret < 0) {
312 	    save_errno = errno;
313 	    krb5_set_error_string(context, "recvfrom %s: %s",
314 				  host, strerror(save_errno));
315 	    return save_errno;
316 	}
317 	len = ret;
318     }
319 
320     if (len < 6) {
321 	str2data (result_string, "server %s sent to too short message "
322 		  "(%d bytes)", host, len);
323 	*result_code = KRB5_KPASSWD_MALFORMED;
324 	return 0;
325     }
326 
327     pkt_len = (reply[0] << 8) | (reply[1]);
328     pkt_ver = (reply[2] << 8) | (reply[3]);
329 
330     if ((pkt_len != len) || (reply[1] == 0x7e || reply[1] == 0x5e)) {
331 	KRB_ERROR error;
332 	size_t size;
333 	u_char *p;
334 
335 	memset(&error, 0, sizeof(error));
336 
337 	ret = decode_KRB_ERROR(reply, len, &error, &size);
338 	if (ret)
339 	    return ret;
340 
341 	if (error.e_data->length < 2) {
342 	    str2data(result_string, "server %s sent too short "
343 		     "e_data to print anything usable", host);
344 	    free_KRB_ERROR(&error);
345 	    *result_code = KRB5_KPASSWD_MALFORMED;
346 	    return 0;
347 	}
348 
349 	p = error.e_data->data;
350 	*result_code = (p[0] << 8) | p[1];
351 	if (error.e_data->length == 2)
352 	    str2data(result_string, "server only sent error code");
353 	else
354 	    krb5_data_copy (result_string,
355 			    p + 2,
356 			    error.e_data->length - 2);
357 	free_KRB_ERROR(&error);
358 	return 0;
359     }
360 
361     if (pkt_len != len) {
362 	str2data (result_string, "client: wrong len in reply");
363 	*result_code = KRB5_KPASSWD_MALFORMED;
364 	return 0;
365     }
366     if (pkt_ver != KRB5_KPASSWD_VERS_CHANGEPW) {
367 	str2data (result_string,
368 		  "client: wrong version number (%d)", pkt_ver);
369 	*result_code = KRB5_KPASSWD_MALFORMED;
370 	return 0;
371     }
372 
373     ap_rep_data.data = reply + 6;
374     ap_rep_data.length  = (reply[4] << 8) | (reply[5]);
375 
376     if (reply + len < (u_char *)ap_rep_data.data + ap_rep_data.length) {
377 	str2data (result_string, "client: wrong AP len in reply");
378 	*result_code = KRB5_KPASSWD_MALFORMED;
379 	return 0;
380     }
381 
382     if (ap_rep_data.length) {
383 	krb5_ap_rep_enc_part *ap_rep;
384 	krb5_data priv_data;
385 	u_char *p;
386 
387 	priv_data.data   = (u_char*)ap_rep_data.data + ap_rep_data.length;
388 	priv_data.length = len - ap_rep_data.length - 6;
389 
390 	ret = krb5_rd_rep (context,
391 			   auth_context,
392 			   &ap_rep_data,
393 			   &ap_rep);
394 	if (ret)
395 	    return ret;
396 
397 	krb5_free_ap_rep_enc_part (context, ap_rep);
398 
399 	ret = krb5_rd_priv (context,
400 			    auth_context,
401 			    &priv_data,
402 			    result_code_string,
403 			    NULL);
404 	if (ret) {
405 	    krb5_data_free (result_code_string);
406 	    return ret;
407 	}
408 
409 	if (result_code_string->length < 2) {
410 	    *result_code = KRB5_KPASSWD_MALFORMED;
411 	    str2data (result_string,
412 		      "client: bad length in result");
413 	    return 0;
414 	}
415 
416         p = result_code_string->data;
417 
418         *result_code = (p[0] << 8) | p[1];
419         krb5_data_copy (result_string,
420                         (unsigned char*)result_code_string->data + 2,
421                         result_code_string->length - 2);
422         return 0;
423     } else {
424 	KRB_ERROR error;
425 	size_t size;
426 	u_char *p;
427 
428 	ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size);
429 	if (ret) {
430 	    return ret;
431 	}
432 	if (error.e_data->length < 2) {
433 	    krb5_warnx (context, "too short e_data to print anything usable");
434 	    return 1;		/* XXX */
435 	}
436 
437 	p = error.e_data->data;
438 	*result_code = (p[0] << 8) | p[1];
439 	krb5_data_copy (result_string,
440 			p + 2,
441 			error.e_data->length - 2);
442 	return 0;
443     }
444 }
445 
446 
447 /*
448  * change the password using the credentials in `creds' (for the
449  * principal indicated in them) to `newpw', storing the result of
450  * the operation in `result_*' and an error code or 0.
451  */
452 
453 typedef krb5_error_code (*kpwd_send_request) (krb5_context,
454 					      krb5_auth_context *,
455 					      krb5_creds *,
456 					      krb5_principal,
457 					      int,
458 					      int,
459 					      char *,
460 					      const char *);
461 typedef krb5_error_code (*kpwd_process_reply) (krb5_context,
462 					       krb5_auth_context,
463 					       int,
464 					       int,
465 					       int *,
466 					       krb5_data *,
467 					       krb5_data *,
468 					       const char *);
469 
470 struct kpwd_proc {
471     const char *name;
472     int flags;
473 #define SUPPORT_TCP	1
474 #define SUPPORT_UDP	2
475     kpwd_send_request send_req;
476     kpwd_process_reply process_rep;
477 } procs[] = {
478     {
479 	"MS set password",
480 	SUPPORT_TCP|SUPPORT_UDP,
481 	setpw_send_request,
482 	process_reply
483     },
484     {
485 	"change password",
486 	SUPPORT_UDP,
487 	chgpw_send_request,
488 	process_reply
489     },
490     { NULL }
491 };
492 
493 static struct kpwd_proc *
494 find_chpw_proto(const char *name)
495 {
496     struct kpwd_proc *p;
497     for (p = procs; p->name != NULL; p++) {
498 	if (strcmp(p->name, name) == 0)
499 	    return p;
500     }
501     return NULL;
502 }
503 
504 /*
505  *
506  */
507 
508 static krb5_error_code
509 change_password_loop (krb5_context	context,
510 		      krb5_creds	*creds,
511 		      krb5_principal	targprinc,
512 		      char		*newpw,
513 		      int		*result_code,
514 		      krb5_data		*result_code_string,
515 		      krb5_data		*result_string,
516 		      struct kpwd_proc	*proc)
517 {
518     krb5_error_code ret;
519     krb5_auth_context auth_context = NULL;
520     krb5_krbhst_handle handle = NULL;
521     krb5_krbhst_info *hi;
522     int sock;
523     int i;
524     int done = 0;
525     krb5_realm realm = creds->client->realm;
526 
527     ret = krb5_auth_con_init (context, &auth_context);
528     if (ret)
529 	return ret;
530 
531     krb5_auth_con_setflags (context, auth_context,
532 			    KRB5_AUTH_CONTEXT_DO_SEQUENCE);
533 
534     ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle);
535     if (ret)
536 	goto out;
537 
538     while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) {
539 	struct addrinfo *ai, *a;
540 	int is_stream;
541 
542 	switch (hi->proto) {
543 	case KRB5_KRBHST_UDP:
544 	    if ((proc->flags & SUPPORT_UDP) == 0)
545 		continue;
546 	    is_stream = 0;
547 	    break;
548 	case KRB5_KRBHST_TCP:
549 	    if ((proc->flags & SUPPORT_TCP) == 0)
550 		continue;
551 	    is_stream = 1;
552 	    break;
553 	default:
554 	    continue;
555 	}
556 
557 	ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
558 	if (ret)
559 	    continue;
560 
561 	for (a = ai; !done && a != NULL; a = a->ai_next) {
562 	    int replied = 0;
563 
564 	    sock = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
565 	    if (sock < 0)
566 		continue;
567 
568 	    ret = connect(sock, a->ai_addr, a->ai_addrlen);
569 	    if (ret < 0) {
570 		close (sock);
571 		goto out;
572 	    }
573 
574 	    ret = krb5_auth_con_genaddrs (context, auth_context, sock,
575 					  KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR);
576 	    if (ret) {
577 		close (sock);
578 		goto out;
579 	    }
580 
581 	    for (i = 0; !done && i < 5; ++i) {
582 		fd_set fdset;
583 		struct timeval tv;
584 
585 		if (!replied) {
586 		    replied = 0;
587 
588 		    ret = (*proc->send_req) (context,
589 					     &auth_context,
590 					     creds,
591 					     targprinc,
592 					     is_stream,
593 					     sock,
594 					     newpw,
595 					     hi->hostname);
596 		    if (ret) {
597 			close(sock);
598 			goto out;
599 		    }
600 		}
601 
602 		if (sock >= FD_SETSIZE) {
603 		    krb5_set_error_string(context, "fd %d too large", sock);
604 		    ret = ERANGE;
605 		    close (sock);
606 		    goto out;
607 		}
608 
609 		FD_ZERO(&fdset);
610 		FD_SET(sock, &fdset);
611 		tv.tv_usec = 0;
612 		tv.tv_sec  = 1 + (1 << i);
613 
614 		ret = select (sock + 1, &fdset, NULL, NULL, &tv);
615 		if (ret < 0 && errno != EINTR) {
616 		    close(sock);
617 		    goto out;
618 		}
619 		if (ret == 1) {
620 		    ret = (*proc->process_rep) (context,
621 						auth_context,
622 						is_stream,
623 						sock,
624 						result_code,
625 						result_code_string,
626 						result_string,
627 						hi->hostname);
628 		    if (ret == 0)
629 			done = 1;
630 		    else if (i > 0 && ret == KRB5KRB_AP_ERR_MUT_FAIL)
631 			replied = 1;
632 		} else {
633 		    ret = KRB5_KDC_UNREACH;
634 		}
635 	    }
636 	    close (sock);
637 	}
638     }
639 
640  out:
641     krb5_krbhst_free (context, handle);
642     krb5_auth_con_free (context, auth_context);
643     if (done)
644 	return 0;
645     else {
646 	if (ret == KRB5_KDC_UNREACH)
647 	    krb5_set_error_string(context,
648 				  "unable to reach any changepw server "
649 				  " in realm %s", realm);
650 	return ret;
651     }
652 }
653 
654 
655 /*
656  * change the password using the credentials in `creds' (for the
657  * principal indicated in them) to `newpw', storing the result of
658  * the operation in `result_*' and an error code or 0.
659  */
660 
661 krb5_error_code
662 krb5_change_password (krb5_context	context,
663 		      krb5_creds	*creds,
664 		      char		*newpw,
665 		      int		*result_code,
666 		      krb5_data		*result_code_string,
667 		      krb5_data		*result_string)
668 {
669     struct kpwd_proc *p = find_chpw_proto("change password");
670 
671     *result_code = KRB5_KPASSWD_MALFORMED;
672     result_code_string->data = result_string->data = NULL;
673     result_code_string->length = result_string->length = 0;
674 
675     if (p == NULL)
676 	return KRB5_KPASSWD_MALFORMED;
677 
678     return change_password_loop(context, creds, NULL, newpw,
679 				result_code, result_code_string,
680 				result_string, p);
681 }
682 
683 /*
684  *
685  */
686 
687 krb5_error_code
688 krb5_set_password(krb5_context context,
689 		  krb5_creds *creds,
690 		  char *newpw,
691 		  krb5_principal targprinc,
692 		  int *result_code,
693 		  krb5_data *result_code_string,
694 		  krb5_data *result_string)
695 {
696     krb5_principal principal = NULL;
697     krb5_error_code ret = 0;
698     int i;
699 
700     *result_code = KRB5_KPASSWD_MALFORMED;
701     result_code_string->data = result_string->data = NULL;
702     result_code_string->length = result_string->length = 0;
703 
704     if (targprinc == NULL) {
705 	ret = krb5_get_default_principal(context, &principal);
706 	if (ret)
707 	    return ret;
708     } else
709 	principal = targprinc;
710 
711     for (i = 0; procs[i].name != NULL; i++) {
712 	*result_code = 0;
713 	ret = change_password_loop(context, creds, targprinc, newpw,
714 				   result_code, result_code_string,
715 				   result_string,
716 				   &procs[i]);
717 	if (ret == 0 && *result_code == 0)
718 	    break;
719     }
720 
721     if (targprinc == NULL)
722 	krb5_free_principal(context, principal);
723     return ret;
724 }
725 
726 /*
727  *
728  */
729 
730 krb5_error_code
731 krb5_set_password_using_ccache(krb5_context context,
732 			       krb5_ccache ccache,
733 			       char *newpw,
734 			       krb5_principal targprinc,
735 			       int *result_code,
736 			       krb5_data *result_code_string,
737 			       krb5_data *result_string)
738 {
739     krb5_creds creds, *credsp;
740     krb5_error_code ret;
741     krb5_principal principal = NULL;
742 
743     *result_code = KRB5_KPASSWD_MALFORMED;
744     result_code_string->data = result_string->data = NULL;
745     result_code_string->length = result_string->length = 0;
746 
747     memset(&creds, 0, sizeof(creds));
748 
749     if (targprinc == NULL) {
750 	ret = krb5_cc_get_principal(context, ccache, &principal);
751 	if (ret)
752 	    return ret;
753     } else
754 	principal = targprinc;
755 
756     ret = krb5_make_principal(context, &creds.server,
757 			      krb5_principal_get_realm(context, principal),
758 			      "kadmin", "changepw", NULL);
759     if (ret)
760 	goto out;
761 
762     ret = krb5_cc_get_principal(context, ccache, &creds.client);
763     if (ret) {
764         krb5_free_principal(context, creds.server);
765 	goto out;
766     }
767 
768     ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
769     krb5_free_principal(context, creds.server);
770     krb5_free_principal(context, creds.client);
771     if (ret)
772 	goto out;
773 
774     ret = krb5_set_password(context,
775 			    credsp,
776 			    newpw,
777 			    principal,
778 			    result_code,
779 			    result_code_string,
780 			    result_string);
781 
782     krb5_free_creds(context, credsp);
783 
784     return ret;
785  out:
786     if (targprinc == NULL)
787 	krb5_free_principal(context, principal);
788     return ret;
789 }
790 
791 /*
792  *
793  */
794 
795 const char*
796 krb5_passwd_result_to_string (krb5_context context,
797 			      int result)
798 {
799     static const char *strings[] = {
800 	"Success",
801 	"Malformed",
802 	"Hard error",
803 	"Auth error",
804 	"Soft error" ,
805 	"Access denied",
806 	"Bad version",
807 	"Initial flag needed"
808     };
809 
810     if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)
811 	return "unknown result code";
812     else
813 	return strings[result];
814 }
815