r23456: Update Samba4 to current lorikeet-heimdal.
[jelmer/samba4-debian.git] / source / heimdal / lib / krb5 / changepw.c
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 #include <krb5_locl.h>
35
36 RCSID("$Id: changepw.c 17442 2006-05-05 09:31:15Z lha $");
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                     const 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   = rk_UNCONST(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                     const 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 = rk_UNCONST(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     uint16_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                   "(%ld bytes)", host, (long)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                                               const 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 static 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                       const 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;
526
527     if (targprinc)
528         realm = targprinc->realm;
529     else
530         realm = creds->client->realm;
531
532     ret = krb5_auth_con_init (context, &auth_context);
533     if (ret)
534         return ret;
535
536     krb5_auth_con_setflags (context, auth_context,
537                             KRB5_AUTH_CONTEXT_DO_SEQUENCE);
538
539     ret = krb5_krbhst_init (context, realm, KRB5_KRBHST_CHANGEPW, &handle);
540     if (ret)
541         goto out;
542
543     while (!done && (ret = krb5_krbhst_next(context, handle, &hi)) == 0) {
544         struct addrinfo *ai, *a;
545         int is_stream;
546
547         switch (hi->proto) {
548         case KRB5_KRBHST_UDP:
549             if ((proc->flags & SUPPORT_UDP) == 0)
550                 continue;
551             is_stream = 0;
552             break;
553         case KRB5_KRBHST_TCP:
554             if ((proc->flags & SUPPORT_TCP) == 0)
555                 continue;
556             is_stream = 1;
557             break;
558         default:
559             continue;
560         }
561
562         ret = krb5_krbhst_get_addrinfo(context, hi, &ai);
563         if (ret)
564             continue;
565
566         for (a = ai; !done && a != NULL; a = a->ai_next) {
567             int replied = 0;
568
569             sock = socket (a->ai_family, a->ai_socktype, a->ai_protocol);
570             if (sock < 0)
571                 continue;
572
573             ret = connect(sock, a->ai_addr, a->ai_addrlen);
574             if (ret < 0) {
575                 close (sock);
576                 goto out;
577             }
578
579             ret = krb5_auth_con_genaddrs (context, auth_context, sock,
580                                           KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR);
581             if (ret) {
582                 close (sock);
583                 goto out;
584             }
585
586             for (i = 0; !done && i < 5; ++i) {
587                 fd_set fdset;
588                 struct timeval tv;
589
590                 if (!replied) {
591                     replied = 0;
592                     
593                     ret = (*proc->send_req) (context,
594                                              &auth_context,
595                                              creds,
596                                              targprinc,
597                                              is_stream,
598                                              sock,
599                                              newpw,
600                                              hi->hostname);
601                     if (ret) {
602                         close(sock);
603                         goto out;
604                     }
605                 }
606             
607                 if (sock >= FD_SETSIZE) {
608                     krb5_set_error_string(context, "fd %d too large", sock);
609                     ret = ERANGE;
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     if (done)
649         return 0;
650     else {
651         if (ret == KRB5_KDC_UNREACH) {
652             krb5_set_error_string(context,
653                                   "unable to reach any changepw server "
654                                   " in realm %s", realm);
655             *result_code = KRB5_KPASSWD_HARDERROR;
656         }
657         return ret;
658     }
659 }
660
661
662 /*
663  * change the password using the credentials in `creds' (for the
664  * principal indicated in them) to `newpw', storing the result of
665  * the operation in `result_*' and an error code or 0.
666  */
667
668 krb5_error_code KRB5_LIB_FUNCTION
669 krb5_change_password (krb5_context      context,
670                       krb5_creds        *creds,
671                       const char        *newpw,
672                       int               *result_code,
673                       krb5_data         *result_code_string,
674                       krb5_data         *result_string)
675 {
676     struct kpwd_proc *p = find_chpw_proto("change password");
677
678     *result_code = KRB5_KPASSWD_MALFORMED;
679     result_code_string->data = result_string->data = NULL;
680     result_code_string->length = result_string->length = 0;
681
682     if (p == NULL)
683         return KRB5_KPASSWD_MALFORMED;
684
685     return change_password_loop(context, creds, NULL, newpw, 
686                                 result_code, result_code_string, 
687                                 result_string, p);
688 }
689
690 /*
691  *
692  */
693
694 krb5_error_code KRB5_LIB_FUNCTION
695 krb5_set_password(krb5_context context,
696                   krb5_creds *creds,
697                   const char *newpw,
698                   krb5_principal targprinc,
699                   int *result_code,
700                   krb5_data *result_code_string,
701                   krb5_data *result_string)
702 {
703     krb5_principal principal = NULL;
704     krb5_error_code ret = 0;
705     int i;
706
707     *result_code = KRB5_KPASSWD_MALFORMED;
708     result_code_string->data = result_string->data = NULL;
709     result_code_string->length = result_string->length = 0;
710
711     if (targprinc == NULL) {
712         ret = krb5_get_default_principal(context, &principal);
713         if (ret)
714             return ret;
715     } else
716         principal = targprinc;
717
718     for (i = 0; procs[i].name != NULL; i++) {
719         *result_code = 0;
720         ret = change_password_loop(context, creds, principal, newpw, 
721                                    result_code, result_code_string, 
722                                    result_string, 
723                                    &procs[i]);
724         if (ret == 0 && *result_code == 0)
725             break;
726     }
727
728     if (targprinc == NULL)
729         krb5_free_principal(context, principal);
730     return ret;
731 }
732
733 /*
734  *
735  */
736
737 krb5_error_code KRB5_LIB_FUNCTION
738 krb5_set_password_using_ccache(krb5_context context,
739                                krb5_ccache ccache,
740                                const char *newpw,
741                                krb5_principal targprinc,
742                                int *result_code,
743                                krb5_data *result_code_string,
744                                krb5_data *result_string)
745 {
746     krb5_creds creds, *credsp;
747     krb5_error_code ret;
748     krb5_principal principal = NULL;
749
750     *result_code = KRB5_KPASSWD_MALFORMED;
751     result_code_string->data = result_string->data = NULL;
752     result_code_string->length = result_string->length = 0;
753
754     memset(&creds, 0, sizeof(creds));
755
756     if (targprinc == NULL) {
757         ret = krb5_cc_get_principal(context, ccache, &principal);
758         if (ret)
759             return ret;
760     } else
761         principal = targprinc;
762
763     ret = krb5_make_principal(context, &creds.server, 
764                               krb5_principal_get_realm(context, principal),
765                               "kadmin", "changepw", NULL);
766     if (ret)
767         goto out;
768
769     ret = krb5_cc_get_principal(context, ccache, &creds.client);
770     if (ret) {
771         krb5_free_principal(context, creds.server);
772         goto out;
773     }
774
775     ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
776     krb5_free_principal(context, creds.server);
777     krb5_free_principal(context, creds.client);
778     if (ret)
779         goto out;
780
781     ret = krb5_set_password(context,
782                             credsp,
783                             newpw,
784                             principal,
785                             result_code,
786                             result_code_string,
787                             result_string);
788
789     krb5_free_creds(context, credsp); 
790
791     return ret;
792  out:
793     if (targprinc == NULL)
794         krb5_free_principal(context, principal);
795     return ret;
796 }
797
798 /*
799  *
800  */
801
802 const char* KRB5_LIB_FUNCTION
803 krb5_passwd_result_to_string (krb5_context context,
804                               int result)
805 {
806     static const char *strings[] = {
807         "Success",
808         "Malformed",
809         "Hard error",
810         "Auth error",
811         "Soft error" ,
812         "Access denied",
813         "Bad version",
814         "Initial flag needed"
815     };
816
817     if (result < 0 || result > KRB5_KPASSWD_INITIAL_FLAG_NEEDED)
818         return "unknown result code";
819     else
820         return strings[result];
821 }