script: Add a script to display testsuite runtime sorted
[Samba.git] / source3 / libads / krb5_setpw.c
blobd84dd5dff9317897b8c4df49bb047b37240e7265
1 /*
2 Unix SMB/CIFS implementation.
3 krb5 set password implementation
4 Copyright (C) Andrew Tridgell 2001
5 Copyright (C) Remus Koos 2001 (remuskoos@yahoo.com)
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 3 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program. If not, see <http://www.gnu.org/licenses/>.
21 #include "includes.h"
22 #include "smb_krb5.h"
23 #include "libads/kerberos_proto.h"
24 #include "../lib/util/asn1.h"
26 #ifdef HAVE_KRB5
28 #define DEFAULT_KPASSWD_PORT 464
30 #define KRB5_KPASSWD_VERS_CHANGEPW 1
32 #define KRB5_KPASSWD_VERS_SETPW 0xff80
33 #define KRB5_KPASSWD_VERS_SETPW_ALT 2
35 #define KRB5_KPASSWD_SUCCESS 0
36 #define KRB5_KPASSWD_MALFORMED 1
37 #define KRB5_KPASSWD_HARDERROR 2
38 #define KRB5_KPASSWD_AUTHERROR 3
39 #define KRB5_KPASSWD_SOFTERROR 4
40 #define KRB5_KPASSWD_ACCESSDENIED 5
41 #define KRB5_KPASSWD_BAD_VERSION 6
42 #define KRB5_KPASSWD_INITIAL_FLAG_NEEDED 7
44 /* Those are defined by kerberos-set-passwd-02.txt and are probably
45 * not supported by M$ implementation */
46 #define KRB5_KPASSWD_POLICY_REJECT 8
47 #define KRB5_KPASSWD_BAD_PRINCIPAL 9
48 #define KRB5_KPASSWD_ETYPE_NOSUPP 10
51 * we've got to be able to distinguish KRB_ERRORs from other
52 * requests - valid response for CHPW v2 replies.
55 #define krb5_is_krb_error(packet) \
56 ( packet && packet->length && (((char *)packet->data)[0] == 0x7e || ((char *)packet->data)[0] == 0x5e))
58 /* This implements kerberos password change protocol as specified in
59 * kerb-chg-password-02.txt and kerberos-set-passwd-02.txt
60 * as well as microsoft version of the protocol
61 * as specified in kerberos-set-passwd-00.txt
63 static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password)
65 char* princ_part1 = NULL;
66 char* princ_part2 = NULL;
67 char* realm = NULL;
68 char* c;
69 char* princ;
71 ASN1_DATA *req;
72 DATA_BLOB ret;
75 princ = SMB_STRDUP(principal);
77 if ((c = strchr_m(princ, '/')) == NULL) {
78 c = princ;
79 } else {
80 *c = '\0';
81 c++;
82 princ_part1 = princ;
85 princ_part2 = c;
87 if ((c = strchr_m(c, '@')) != NULL) {
88 *c = '\0';
89 c++;
90 realm = c;
91 } else {
92 /* We must have a realm component. */
93 return data_blob_null;
96 req = asn1_init(talloc_tos());
97 if (req == NULL) {
98 return data_blob_null;
101 asn1_push_tag(req, ASN1_SEQUENCE(0));
102 asn1_push_tag(req, ASN1_CONTEXT(0));
103 asn1_write_OctetString(req, password, strlen(password));
104 asn1_pop_tag(req);
106 asn1_push_tag(req, ASN1_CONTEXT(1));
107 asn1_push_tag(req, ASN1_SEQUENCE(0));
109 asn1_push_tag(req, ASN1_CONTEXT(0));
110 asn1_write_Integer(req, 1);
111 asn1_pop_tag(req);
113 asn1_push_tag(req, ASN1_CONTEXT(1));
114 asn1_push_tag(req, ASN1_SEQUENCE(0));
116 if (princ_part1) {
117 asn1_write_GeneralString(req, princ_part1);
120 asn1_write_GeneralString(req, princ_part2);
121 asn1_pop_tag(req);
122 asn1_pop_tag(req);
123 asn1_pop_tag(req);
124 asn1_pop_tag(req);
126 asn1_push_tag(req, ASN1_CONTEXT(2));
127 asn1_write_GeneralString(req, realm);
128 asn1_pop_tag(req);
129 asn1_pop_tag(req);
131 ret = data_blob(req->data, req->length);
132 asn1_free(req);
134 free(princ);
136 return ret;
139 static krb5_error_code build_kpasswd_request(uint16 pversion,
140 krb5_context context,
141 krb5_auth_context auth_context,
142 krb5_data *ap_req,
143 const char *princ,
144 const char *passwd,
145 bool use_tcp,
146 krb5_data *packet)
148 krb5_error_code ret;
149 krb5_data cipherpw;
150 krb5_data encoded_setpw;
151 krb5_replay_data replay;
152 char *p, *msg_start;
153 DATA_BLOB setpw;
154 unsigned int msg_length;
156 ret = krb5_auth_con_setflags(context,
157 auth_context,KRB5_AUTH_CONTEXT_DO_SEQUENCE);
158 if (ret) {
159 DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
160 error_message(ret)));
161 return ret;
164 /* handle protocol differences in chpw and setpw */
165 if (pversion == KRB5_KPASSWD_VERS_CHANGEPW)
166 setpw = data_blob(passwd, strlen(passwd));
167 else if (pversion == KRB5_KPASSWD_VERS_SETPW ||
168 pversion == KRB5_KPASSWD_VERS_SETPW_ALT)
169 setpw = encode_krb5_setpw(princ, passwd);
170 else
171 return EINVAL;
173 if (setpw.data == NULL || setpw.length == 0) {
174 return EINVAL;
177 encoded_setpw.data = (char *)setpw.data;
178 encoded_setpw.length = setpw.length;
180 ret = krb5_mk_priv(context, auth_context,
181 &encoded_setpw, &cipherpw, &replay);
183 data_blob_free(&setpw); /*from 'encode_krb5_setpw(...)' */
185 if (ret) {
186 DEBUG(1,("krb5_mk_priv failed (%s)\n", error_message(ret)));
187 return ret;
190 packet->data = (char *)SMB_MALLOC(ap_req->length + cipherpw.length + (use_tcp ? 10 : 6 ));
191 if (!packet->data)
192 return -1;
196 /* see the RFC for details */
198 msg_start = p = ((char *)packet->data) + (use_tcp ? 4 : 0);
199 p += 2;
200 RSSVAL(p, 0, pversion);
201 p += 2;
202 RSSVAL(p, 0, ap_req->length);
203 p += 2;
204 memcpy(p, ap_req->data, ap_req->length);
205 p += ap_req->length;
206 memcpy(p, cipherpw.data, cipherpw.length);
207 p += cipherpw.length;
208 packet->length = PTR_DIFF(p,packet->data);
209 msg_length = PTR_DIFF(p,msg_start);
211 if (use_tcp) {
212 RSIVAL(packet->data, 0, msg_length);
214 RSSVAL(msg_start, 0, msg_length);
216 free(cipherpw.data); /* from 'krb5_mk_priv(...)' */
218 return 0;
221 static const struct kpasswd_errors {
222 int result_code;
223 const char *error_string;
224 } kpasswd_errors[] = {
225 {KRB5_KPASSWD_MALFORMED, "Malformed request error"},
226 {KRB5_KPASSWD_HARDERROR, "Server error"},
227 {KRB5_KPASSWD_AUTHERROR, "Authentication error"},
228 {KRB5_KPASSWD_SOFTERROR, "Password change rejected"},
229 {KRB5_KPASSWD_ACCESSDENIED, "Client does not have proper authorization"},
230 {KRB5_KPASSWD_BAD_VERSION, "Protocol version not supported"},
231 {KRB5_KPASSWD_INITIAL_FLAG_NEEDED, "Authorization ticket must have initial flag set"},
232 {KRB5_KPASSWD_POLICY_REJECT, "Password rejected due to policy requirements"},
233 {KRB5_KPASSWD_BAD_PRINCIPAL, "Target principal does not exist"},
234 {KRB5_KPASSWD_ETYPE_NOSUPP, "Unsupported encryption type"},
235 {0, NULL}
238 static krb5_error_code setpw_result_code_string(krb5_context context,
239 int result_code,
240 const char **code_string)
242 unsigned int idx = 0;
244 while (kpasswd_errors[idx].error_string != NULL) {
245 if (kpasswd_errors[idx].result_code ==
246 result_code) {
247 *code_string = kpasswd_errors[idx].error_string;
248 return 0;
250 idx++;
252 *code_string = "Password change failed";
253 return (0);
256 krb5_error_code kpasswd_err_to_krb5_err(krb5_error_code res_code)
258 switch(res_code) {
259 case KRB5_KPASSWD_ACCESSDENIED:
260 return KRB5KDC_ERR_BADOPTION;
261 case KRB5_KPASSWD_INITIAL_FLAG_NEEDED:
262 return KRB5KDC_ERR_BADOPTION;
263 /* return KV5M_ALT_METHOD; MIT-only define */
264 case KRB5_KPASSWD_ETYPE_NOSUPP:
265 return KRB5KDC_ERR_ETYPE_NOSUPP;
266 case KRB5_KPASSWD_BAD_PRINCIPAL:
267 return KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN;
268 case KRB5_KPASSWD_POLICY_REJECT:
269 case KRB5_KPASSWD_SOFTERROR:
270 return KRB5KDC_ERR_POLICY;
271 default:
272 return KRB5KRB_ERR_GENERIC;
275 static krb5_error_code parse_setpw_reply(krb5_context context,
276 bool use_tcp,
277 krb5_auth_context auth_context,
278 krb5_data *packet)
280 krb5_data ap_rep;
281 char *p;
282 int vnum, ret, res_code;
283 krb5_data cipherresult;
284 krb5_data clearresult;
285 krb5_ap_rep_enc_part *ap_rep_enc;
286 krb5_replay_data replay;
287 unsigned int msg_length = packet->length;
290 if (packet->length < (use_tcp ? 8 : 4)) {
291 return KRB5KRB_AP_ERR_MODIFIED;
294 p = (char *)packet->data;
296 ** see if it is an error
298 if (krb5_is_krb_error(packet)) {
300 ret = handle_krberror_packet(context, packet);
301 if (ret) {
302 return ret;
307 /* tcp... */
308 if (use_tcp) {
309 msg_length -= 4;
310 if (RIVAL(p, 0) != msg_length) {
311 DEBUG(1,("Bad TCP packet length (%d/%d) from kpasswd server\n",
312 RIVAL(p, 0), msg_length));
313 return KRB5KRB_AP_ERR_MODIFIED;
316 p += 4;
319 if (RSVAL(p, 0) != msg_length) {
320 DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n",
321 RSVAL(p, 0), msg_length));
322 return KRB5KRB_AP_ERR_MODIFIED;
325 p += 2;
327 vnum = RSVAL(p, 0); p += 2;
329 /* FIXME: According to standard there is only one type of reply */
330 if (vnum != KRB5_KPASSWD_VERS_SETPW &&
331 vnum != KRB5_KPASSWD_VERS_SETPW_ALT &&
332 vnum != KRB5_KPASSWD_VERS_CHANGEPW) {
333 DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum));
334 return KRB5KDC_ERR_BAD_PVNO;
337 ap_rep.length = RSVAL(p, 0); p += 2;
339 if (p + ap_rep.length >= (char *)packet->data + packet->length) {
340 DEBUG(1,("ptr beyond end of packet from kpasswd server\n"));
341 return KRB5KRB_AP_ERR_MODIFIED;
344 if (ap_rep.length == 0) {
345 DEBUG(1,("got unencrypted setpw result?!\n"));
346 return KRB5KRB_AP_ERR_MODIFIED;
349 /* verify ap_rep */
350 ap_rep.data = p;
351 p += ap_rep.length;
353 ret = krb5_rd_rep(context, auth_context, &ap_rep, &ap_rep_enc);
354 if (ret) {
355 DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret)));
356 return KRB5KRB_AP_ERR_MODIFIED;
359 krb5_free_ap_rep_enc_part(context, ap_rep_enc);
361 cipherresult.data = p;
362 cipherresult.length = ((char *)packet->data + packet->length) - p;
364 ret = krb5_rd_priv(context, auth_context, &cipherresult, &clearresult,
365 &replay);
366 if (ret) {
367 DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret)));
368 return KRB5KRB_AP_ERR_MODIFIED;
371 if (clearresult.length < 2) {
372 free(clearresult.data);
373 ret = KRB5KRB_AP_ERR_MODIFIED;
374 return KRB5KRB_AP_ERR_MODIFIED;
377 p = (char *)clearresult.data;
379 res_code = RSVAL(p, 0);
381 free(clearresult.data);
383 if ((res_code < KRB5_KPASSWD_SUCCESS) ||
384 (res_code > KRB5_KPASSWD_ETYPE_NOSUPP)) {
385 return KRB5KRB_AP_ERR_MODIFIED;
388 if (res_code == KRB5_KPASSWD_SUCCESS) {
389 return 0;
390 } else {
391 const char *errstr;
392 setpw_result_code_string(context, res_code, &errstr);
393 DEBUG(1, ("Error changing password: %s (%d)\n", errstr, res_code));
395 return kpasswd_err_to_krb5_err(res_code);
399 static ADS_STATUS do_krb5_kpasswd_request(krb5_context context,
400 const char *kdc_host,
401 uint16 pversion,
402 krb5_creds *credsp,
403 const char *princ,
404 const char *newpw)
406 krb5_auth_context auth_context = NULL;
407 krb5_data ap_req, chpw_req, chpw_rep;
408 int ret, sock;
409 socklen_t addr_len;
410 struct sockaddr_storage remote_addr, local_addr;
411 struct sockaddr_storage addr;
412 krb5_address local_kaddr, remote_kaddr;
413 bool use_tcp = False;
416 if (!interpret_string_addr(&addr, kdc_host, 0)) {
419 ret = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
420 NULL, credsp, &ap_req);
421 if (ret) {
422 DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret)));
423 return ADS_ERROR_KRB5(ret);
426 do {
428 if (!use_tcp) {
430 sock = open_udp_socket(kdc_host, DEFAULT_KPASSWD_PORT);
431 if (sock == -1) {
432 int rc = errno;
433 SAFE_FREE(ap_req.data);
434 krb5_auth_con_free(context, auth_context);
435 DEBUG(1,("failed to open kpasswd socket to %s "
436 "(%s)\n", kdc_host, strerror(errno)));
437 return ADS_ERROR_SYSTEM(rc);
439 } else {
440 NTSTATUS status;
441 status = open_socket_out(&addr, DEFAULT_KPASSWD_PORT,
442 LONG_CONNECT_TIMEOUT, &sock);
443 if (!NT_STATUS_IS_OK(status)) {
444 SAFE_FREE(ap_req.data);
445 krb5_auth_con_free(context, auth_context);
446 DEBUG(1,("failed to open kpasswd socket to %s "
447 "(%s)\n", kdc_host,
448 nt_errstr(status)));
449 return ADS_ERROR_NT(status);
453 addr_len = sizeof(remote_addr);
454 if (getpeername(sock, (struct sockaddr *)&remote_addr, &addr_len) != 0) {
455 close(sock);
456 SAFE_FREE(ap_req.data);
457 krb5_auth_con_free(context, auth_context);
458 DEBUG(1,("getpeername() failed (%s)\n", error_message(errno)));
459 return ADS_ERROR_SYSTEM(errno);
461 addr_len = sizeof(local_addr);
462 if (getsockname(sock, (struct sockaddr *)&local_addr, &addr_len) != 0) {
463 close(sock);
464 SAFE_FREE(ap_req.data);
465 krb5_auth_con_free(context, auth_context);
466 DEBUG(1,("getsockname() failed (%s)\n", error_message(errno)));
467 return ADS_ERROR_SYSTEM(errno);
469 if (!setup_kaddr(&remote_kaddr, &remote_addr) ||
470 !setup_kaddr(&local_kaddr, &local_addr)) {
471 DEBUG(1,("do_krb5_kpasswd_request: "
472 "Failed to setup addresses.\n"));
473 close(sock);
474 SAFE_FREE(ap_req.data);
475 krb5_auth_con_free(context, auth_context);
476 errno = EINVAL;
477 return ADS_ERROR_SYSTEM(EINVAL);
480 ret = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr, NULL);
481 if (ret) {
482 close(sock);
483 SAFE_FREE(ap_req.data);
484 krb5_auth_con_free(context, auth_context);
485 DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret)));
486 return ADS_ERROR_KRB5(ret);
489 ret = build_kpasswd_request(pversion, context, auth_context, &ap_req,
490 princ, newpw, use_tcp, &chpw_req);
491 if (ret) {
492 close(sock);
493 SAFE_FREE(ap_req.data);
494 krb5_auth_con_free(context, auth_context);
495 DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret)));
496 return ADS_ERROR_KRB5(ret);
499 ret = write(sock, chpw_req.data, chpw_req.length);
501 if (ret != chpw_req.length) {
502 close(sock);
503 SAFE_FREE(chpw_req.data);
504 SAFE_FREE(ap_req.data);
505 krb5_auth_con_free(context, auth_context);
506 DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
507 return ADS_ERROR_SYSTEM(errno);
510 SAFE_FREE(chpw_req.data);
512 chpw_rep.length = 1500;
513 chpw_rep.data = (char *) SMB_MALLOC(chpw_rep.length);
514 if (!chpw_rep.data) {
515 close(sock);
516 SAFE_FREE(ap_req.data);
517 krb5_auth_con_free(context, auth_context);
518 DEBUG(1,("send of chpw failed (%s)\n", strerror(errno)));
519 errno = ENOMEM;
520 return ADS_ERROR_SYSTEM(errno);
523 ret = read(sock, chpw_rep.data, chpw_rep.length);
524 if (ret < 0) {
525 close(sock);
526 SAFE_FREE(chpw_rep.data);
527 SAFE_FREE(ap_req.data);
528 krb5_auth_con_free(context, auth_context);
529 DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno)));
530 return ADS_ERROR_SYSTEM(errno);
533 close(sock);
534 chpw_rep.length = ret;
536 ret = krb5_auth_con_setaddrs(context, auth_context, NULL,&remote_kaddr);
537 if (ret) {
538 SAFE_FREE(chpw_rep.data);
539 SAFE_FREE(ap_req.data);
540 krb5_auth_con_free(context, auth_context);
541 DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n",
542 error_message(ret)));
543 return ADS_ERROR_KRB5(ret);
546 ret = parse_setpw_reply(context, use_tcp, auth_context, &chpw_rep);
547 SAFE_FREE(chpw_rep.data);
549 if (ret) {
551 if (ret == KRB5KRB_ERR_RESPONSE_TOO_BIG && !use_tcp) {
552 DEBUG(5, ("Trying setpw with TCP!!!\n"));
553 use_tcp = True;
554 continue;
557 SAFE_FREE(ap_req.data);
558 krb5_auth_con_free(context, auth_context);
559 DEBUG(1,("parse_setpw_reply failed (%s)\n",
560 error_message(ret)));
561 return ADS_ERROR_KRB5(ret);
564 SAFE_FREE(ap_req.data);
565 krb5_auth_con_free(context, auth_context);
566 } while ( ret );
568 return ADS_SUCCESS;
571 ADS_STATUS ads_krb5_set_password(const char *kdc_host, const char *princ,
572 const char *newpw, int time_offset)
575 ADS_STATUS aret;
576 krb5_error_code ret = 0;
577 krb5_context context = NULL;
578 const char *realm = NULL;
579 unsigned int realm_len = 0;
580 krb5_creds creds, *credsp = NULL;
581 krb5_ccache ccache = NULL;
583 ZERO_STRUCT(creds);
585 initialize_krb5_error_table();
586 ret = krb5_init_context(&context);
587 if (ret) {
588 DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
589 return ADS_ERROR_KRB5(ret);
592 if (time_offset != 0) {
593 krb5_set_real_time(context, time(NULL) + time_offset, 0);
596 ret = krb5_cc_default(context, &ccache);
597 if (ret) {
598 krb5_free_context(context);
599 DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret)));
600 return ADS_ERROR_KRB5(ret);
603 ret = krb5_cc_get_principal(context, ccache, &creds.client);
604 if (ret) {
605 krb5_cc_close(context, ccache);
606 krb5_free_context(context);
607 DEBUG(1,("Failed to get principal from ccache (%s)\n",
608 error_message(ret)));
609 return ADS_ERROR_KRB5(ret);
612 realm = smb_krb5_principal_get_realm(context, creds.client);
613 realm_len = strlen(realm);
614 ret = krb5_build_principal(context,
615 &creds.server,
616 realm_len,
617 realm, "kadmin", "changepw", NULL);
619 ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
620 if (ret) {
621 krb5_cc_close(context, ccache);
622 krb5_free_principal(context, creds.client);
623 krb5_free_principal(context, creds.server);
624 krb5_free_context(context);
625 DEBUG(1,("krb5_build_prinipal_ext (%s)\n", error_message(ret)));
626 return ADS_ERROR_KRB5(ret);
629 ret = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
630 if (ret) {
631 krb5_cc_close(context, ccache);
632 krb5_free_principal(context, creds.client);
633 krb5_free_principal(context, creds.server);
634 krb5_free_context(context);
635 DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret)));
636 return ADS_ERROR_KRB5(ret);
639 /* we might have to call krb5_free_creds(...) from now on ... */
641 aret = do_krb5_kpasswd_request(context, kdc_host,
642 KRB5_KPASSWD_VERS_SETPW,
643 credsp, princ, newpw);
645 krb5_free_creds(context, credsp);
646 krb5_free_principal(context, creds.client);
647 krb5_free_principal(context, creds.server);
648 krb5_cc_close(context, ccache);
649 krb5_free_context(context);
651 return aret;
655 we use a prompter to avoid a crash bug in the kerberos libs when
656 dealing with empty passwords
657 this prompter is just a string copy ...
659 static krb5_error_code
660 kerb_prompter(krb5_context ctx, void *data,
661 const char *name,
662 const char *banner,
663 int num_prompts,
664 krb5_prompt prompts[])
666 if (num_prompts == 0) return 0;
668 memset(prompts[0].reply->data, 0, prompts[0].reply->length);
669 if (prompts[0].reply->length > 0) {
670 if (data) {
671 strncpy((char *)prompts[0].reply->data,
672 (const char *)data,
673 prompts[0].reply->length-1);
674 prompts[0].reply->length = strlen((const char *)prompts[0].reply->data);
675 } else {
676 prompts[0].reply->length = 0;
679 return 0;
682 static ADS_STATUS ads_krb5_chg_password(const char *kdc_host,
683 const char *principal,
684 const char *oldpw,
685 const char *newpw,
686 int time_offset)
688 ADS_STATUS aret;
689 krb5_error_code ret;
690 krb5_context context = NULL;
691 krb5_principal princ;
692 krb5_get_init_creds_opt opts;
693 krb5_creds creds;
694 char *chpw_princ = NULL, *password;
695 const char *realm = NULL;
697 initialize_krb5_error_table();
698 ret = krb5_init_context(&context);
699 if (ret) {
700 DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret)));
701 return ADS_ERROR_KRB5(ret);
704 if ((ret = smb_krb5_parse_name(context, principal,
705 &princ))) {
706 krb5_free_context(context);
707 DEBUG(1,("Failed to parse %s (%s)\n", principal, error_message(ret)));
708 return ADS_ERROR_KRB5(ret);
711 krb5_get_init_creds_opt_init(&opts);
712 krb5_get_init_creds_opt_set_tkt_life(&opts, 5*60);
713 krb5_get_init_creds_opt_set_renew_life(&opts, 0);
714 krb5_get_init_creds_opt_set_forwardable(&opts, 0);
715 krb5_get_init_creds_opt_set_proxiable(&opts, 0);
717 realm = smb_krb5_principal_get_realm(context, princ);
719 /* We have to obtain an INITIAL changepw ticket for changing password */
720 if (asprintf(&chpw_princ, "kadmin/changepw@%s", realm) == -1) {
721 krb5_free_context(context);
722 DEBUG(1,("ads_krb5_chg_password: asprintf fail\n"));
723 return ADS_ERROR_NT(NT_STATUS_NO_MEMORY);
726 password = SMB_STRDUP(oldpw);
727 ret = krb5_get_init_creds_password(context, &creds, princ, password,
728 kerb_prompter, NULL,
729 0, chpw_princ, &opts);
730 SAFE_FREE(chpw_princ);
731 SAFE_FREE(password);
733 if (ret) {
734 if (ret == KRB5KRB_AP_ERR_BAD_INTEGRITY)
735 DEBUG(1,("Password incorrect while getting initial ticket"));
736 else
737 DEBUG(1,("krb5_get_init_creds_password failed (%s)\n", error_message(ret)));
739 krb5_free_principal(context, princ);
740 krb5_free_context(context);
741 return ADS_ERROR_KRB5(ret);
744 aret = do_krb5_kpasswd_request(context, kdc_host,
745 KRB5_KPASSWD_VERS_CHANGEPW,
746 &creds, principal, newpw);
748 krb5_free_principal(context, princ);
749 krb5_free_context(context);
751 return aret;
755 ADS_STATUS kerberos_set_password(const char *kpasswd_server,
756 const char *auth_principal, const char *auth_password,
757 const char *target_principal, const char *new_password,
758 int time_offset)
760 int ret;
762 if ((ret = kerberos_kinit_password(auth_principal, auth_password, time_offset, NULL))) {
763 DEBUG(1,("Failed kinit for principal %s (%s)\n", auth_principal, error_message(ret)));
764 return ADS_ERROR_KRB5(ret);
767 if (!strcmp(auth_principal, target_principal))
768 return ads_krb5_chg_password(kpasswd_server, target_principal,
769 auth_password, new_password, time_offset);
770 else
771 return ads_krb5_set_password(kpasswd_server, target_principal,
772 new_password, time_offset);
775 #endif