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