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 2 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, write to the Free Software
19 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
26 #define DEFAULT_KPASSWD_PORT 464
27 #define KRB5_KPASSWD_VERS_CHANGEPW 1
28 #define KRB5_KPASSWD_VERS_SETPW 0xff80
29 #define KRB5_KPASSWD_ACCESSDENIED 5
30 #define KRB5_KPASSWD_BAD_VERSION 6
32 /* This implements the Kerb password change protocol as specifed in
33 * kerb-chg-password-02.txt
35 static DATA_BLOB
encode_krb5_setpw(const char *principal
, const char *password
)
37 char* princ_part1
= NULL
;
38 char* princ_part2
= NULL
;
47 princ
= strdup(principal
);
49 if ((c
= strchr(princ
, '/')) == NULL
) {
59 if ((c
= strchr(c
, '@')) != NULL
) {
65 memset(&req
, 0, sizeof(req
));
67 asn1_push_tag(&req
, ASN1_SEQUENCE(0));
68 asn1_push_tag(&req
, ASN1_CONTEXT(0));
69 asn1_write_OctetString(&req
, password
, strlen(password
));
72 asn1_push_tag(&req
, ASN1_CONTEXT(1));
73 asn1_push_tag(&req
, ASN1_SEQUENCE(0));
75 asn1_push_tag(&req
, ASN1_CONTEXT(0));
76 asn1_write_Integer(&req
, 1);
79 asn1_push_tag(&req
, ASN1_CONTEXT(1));
80 asn1_push_tag(&req
, ASN1_SEQUENCE(0));
83 asn1_write_GeneralString(&req
, princ_part1
);
85 asn1_write_GeneralString(&req
, princ_part2
);
91 asn1_push_tag(&req
, ASN1_CONTEXT(2));
92 asn1_write_GeneralString(&req
, realm
);
96 ret
= data_blob(req
.data
, req
.length
);
104 static krb5_error_code
build_setpw_request(krb5_context context
,
105 krb5_auth_context auth_context
,
113 krb5_data encoded_setpw
;
114 krb5_replay_data replay
;
118 ret
= krb5_auth_con_setflags(context
,
119 auth_context
,KRB5_AUTH_CONTEXT_DO_SEQUENCE
);
121 DEBUG(1,("krb5_auth_con_setflags failed (%s)\n",
122 error_message(ret
)));
126 setpw
= encode_krb5_setpw(princ
, passwd
);
128 encoded_setpw
.data
= setpw
.data
;
129 encoded_setpw
.length
= setpw
.length
;
131 ret
= krb5_mk_priv(context
, auth_context
,
132 &encoded_setpw
, &cipherpw
, &replay
);
134 data_blob_free(&setpw
); /*from 'encode_krb5_setpw(...)' */
137 DEBUG(1,("krb5_mk_priv failed (%s)\n", error_message(ret
)));
141 packet
->data
= (char *)malloc(ap_req
->length
+ cipherpw
.length
+ 6);
143 /* see the RFC for details */
144 p
= packet
->data
+ 2;
145 RSSVAL(p
, 0, 0xff80); p
+= 2;
146 RSSVAL(p
, 0, ap_req
->length
); p
+= 2;
147 memcpy(p
, ap_req
->data
, ap_req
->length
); p
+= ap_req
->length
;
148 memcpy(p
, cipherpw
.data
, cipherpw
.length
); p
+= cipherpw
.length
;
149 packet
->length
= PTR_DIFF(p
,packet
->data
);
150 RSSVAL(packet
->data
, 0, packet
->length
);
152 free(cipherpw
.data
); /* from 'krb5_mk_priv(...)' */
157 static krb5_error_code
parse_setpw_reply(krb5_context context
,
158 krb5_auth_context auth_context
,
163 int vnum
, ret
, res_code
;
164 krb5_data cipherresult
;
165 krb5_data clearresult
;
166 krb5_ap_rep_enc_part
*ap_rep_enc
;
167 krb5_replay_data replay
;
169 if (packet
->length
< 4) {
170 return KRB5KRB_AP_ERR_MODIFIED
;
175 if (packet
->data
[0] == 0x7e || packet
->data
[0] == 0x5e) {
176 /* it's an error packet. We should parse it ... */
177 DEBUG(1,("Got error packet 0x%x from kpasswd server\n",
179 return KRB5KRB_AP_ERR_MODIFIED
;
182 if (RSVAL(p
, 0) != packet
->length
) {
183 DEBUG(1,("Bad packet length (%d/%d) from kpasswd server\n",
184 RSVAL(p
, 0), packet
->length
));
185 return KRB5KRB_AP_ERR_MODIFIED
;
190 vnum
= RSVAL(p
, 0); p
+= 2;
192 if (vnum
!= KRB5_KPASSWD_VERS_SETPW
&& vnum
!= KRB5_KPASSWD_VERS_CHANGEPW
) {
193 DEBUG(1,("Bad vnum (%d) from kpasswd server\n", vnum
));
194 return KRB5KDC_ERR_BAD_PVNO
;
197 ap_rep
.length
= RSVAL(p
, 0); p
+= 2;
199 if (p
+ ap_rep
.length
>= packet
->data
+ packet
->length
) {
200 DEBUG(1,("ptr beyond end of packet from kpasswd server\n"));
201 return KRB5KRB_AP_ERR_MODIFIED
;
204 if (ap_rep
.length
== 0) {
205 DEBUG(1,("got unencrypted setpw result?!\n"));
206 return KRB5KRB_AP_ERR_MODIFIED
;
213 ret
= krb5_rd_rep(context
, auth_context
, &ap_rep
, &ap_rep_enc
);
215 DEBUG(1,("failed to rd setpw reply (%s)\n", error_message(ret
)));
216 return KRB5KRB_AP_ERR_MODIFIED
;
219 krb5_free_ap_rep_enc_part(context
, ap_rep_enc
);
221 cipherresult
.data
= p
;
222 cipherresult
.length
= (packet
->data
+ packet
->length
) - p
;
224 ret
= krb5_rd_priv(context
, auth_context
, &cipherresult
, &clearresult
,
227 DEBUG(1,("failed to decrypt setpw reply (%s)\n", error_message(ret
)));
228 return KRB5KRB_AP_ERR_MODIFIED
;
231 if (clearresult
.length
< 2) {
232 free(clearresult
.data
);
233 ret
= KRB5KRB_AP_ERR_MODIFIED
;
234 return KRB5KRB_AP_ERR_MODIFIED
;
237 p
= clearresult
.data
;
239 res_code
= RSVAL(p
, 0);
241 free(clearresult
.data
);
243 if ((res_code
< KRB5_KPASSWD_SUCCESS
) ||
244 (res_code
>= KRB5_KPASSWD_ACCESSDENIED
)) {
245 return KRB5KRB_AP_ERR_MODIFIED
;
251 ADS_STATUS
krb5_set_password(const char *kdc_host
, const char *princ
, const char *newpw
,
254 krb5_context context
;
255 krb5_auth_context auth_context
= NULL
;
256 krb5_principal principal
;
259 krb5_creds creds
, *credsp
;
261 krb5_data ap_req
, chpw_req
, chpw_rep
;
262 int ret
, sock
, addr_len
;
263 struct sockaddr remote_addr
, local_addr
;
264 krb5_address local_kaddr
, remote_kaddr
;
266 ret
= krb5_init_context(&context
);
268 DEBUG(1,("Failed to init krb5 context (%s)\n", error_message(ret
)));
269 return ADS_ERROR_KRB5(ret
);
272 if (time_offset
!= 0) {
273 krb5_set_real_time(context
, time(NULL
) + time_offset
, 0);
276 ret
= krb5_cc_default(context
, &ccache
);
278 krb5_free_context(context
);
279 DEBUG(1,("Failed to get default creds (%s)\n", error_message(ret
)));
280 return ADS_ERROR_KRB5(ret
);
285 realm
= strchr(princ
, '@');
288 asprintf(&princ_name
, "kadmin/changepw@%s", realm
);
289 ret
= krb5_parse_name(context
, princ_name
, &creds
.server
);
291 krb5_free_context(context
);
292 DEBUG(1,("Failed to parse kadmin/changepw (%s)\n", error_message(ret
)));
293 return ADS_ERROR_KRB5(ret
);
297 /* parse the principal we got as a function argument */
298 ret
= krb5_parse_name(context
, princ
, &principal
);
300 krb5_free_context(context
);
301 DEBUG(1,("Failed to parse %s (%s)\n", princ_name
, error_message(ret
)));
302 return ADS_ERROR_KRB5(ret
);
305 krb5_princ_set_realm(context
, creds
.server
,
306 krb5_princ_realm(context
, principal
));
308 ret
= krb5_cc_get_principal(context
, ccache
, &creds
.client
);
310 krb5_free_principal(context
, principal
);
311 krb5_free_context(context
);
312 DEBUG(1,("Failed to get principal from ccache (%s)\n",
313 error_message(ret
)));
314 return ADS_ERROR_KRB5(ret
);
317 ret
= krb5_get_credentials(context
, 0, ccache
, &creds
, &credsp
);
319 krb5_free_principal(context
, creds
.client
);
320 krb5_free_principal(context
, principal
);
321 krb5_free_context(context
);
322 DEBUG(1,("krb5_get_credentials failed (%s)\n", error_message(ret
)));
323 return ADS_ERROR_KRB5(ret
);
326 /* we might have to call krb5_free_creds(...) from now on ... */
327 ret
= krb5_mk_req_extended(context
, &auth_context
, AP_OPTS_USE_SUBKEY
,
328 NULL
, credsp
, &ap_req
);
330 krb5_free_creds(context
, credsp
);
331 krb5_free_principal(context
, creds
.client
);
332 krb5_free_principal(context
, principal
);
333 krb5_free_context(context
);
334 DEBUG(1,("krb5_mk_req_extended failed (%s)\n", error_message(ret
)));
335 return ADS_ERROR_KRB5(ret
);
338 sock
= open_udp_socket(kdc_host
, DEFAULT_KPASSWD_PORT
);
342 krb5_free_creds(context
, credsp
);
343 krb5_free_principal(context
, creds
.client
);
344 krb5_free_principal(context
, principal
);
345 krb5_free_context(context
);
346 DEBUG(1,("failed to open kpasswd socket to %s (%s)\n",
347 kdc_host
, strerror(errno
)));
348 return ADS_ERROR_SYSTEM(rc
);
351 addr_len
= sizeof(remote_addr
);
352 getpeername(sock
, &remote_addr
, &addr_len
);
353 addr_len
= sizeof(local_addr
);
354 getsockname(sock
, &local_addr
, &addr_len
);
356 remote_kaddr
.addrtype
= ADDRTYPE_INET
;
357 remote_kaddr
.length
= sizeof(((struct sockaddr_in
*)&remote_addr
)->sin_addr
);
358 remote_kaddr
.contents
= (char *)&(((struct sockaddr_in
*)&remote_addr
)->sin_addr
);
359 local_kaddr
.addrtype
= ADDRTYPE_INET
;
360 local_kaddr
.length
= sizeof(((struct sockaddr_in
*)&local_addr
)->sin_addr
);
361 local_kaddr
.contents
= (char *)&(((struct sockaddr_in
*)&local_addr
)->sin_addr
);
363 ret
= krb5_auth_con_setaddrs(context
, auth_context
, &local_kaddr
, NULL
);
367 krb5_free_creds(context
, credsp
);
368 krb5_free_principal(context
, creds
.client
);
369 krb5_free_principal(context
, principal
);
370 krb5_free_context(context
);
371 DEBUG(1,("krb5_auth_con_setaddrs failed (%s)\n", error_message(ret
)));
372 return ADS_ERROR_KRB5(ret
);
375 ret
= build_setpw_request(context
, auth_context
, &ap_req
,
376 princ
, newpw
, &chpw_req
);
380 krb5_free_creds(context
, credsp
);
381 krb5_free_principal(context
, creds
.client
);
382 krb5_free_principal(context
, principal
);
383 krb5_free_context(context
);
384 DEBUG(1,("build_setpw_request failed (%s)\n", error_message(ret
)));
385 return ADS_ERROR_KRB5(ret
);
388 if (write(sock
, chpw_req
.data
, chpw_req
.length
) != chpw_req
.length
) {
392 krb5_free_creds(context
, credsp
);
393 krb5_free_principal(context
, creds
.client
);
394 krb5_free_principal(context
, principal
);
395 krb5_free_context(context
);
396 DEBUG(1,("send of chpw failed (%s)\n", strerror(errno
)));
397 return ADS_ERROR_SYSTEM(errno
);
402 chpw_rep
.length
= 1500;
403 chpw_rep
.data
= (char *) malloc(chpw_rep
.length
);
405 ret
= read(sock
, chpw_rep
.data
, chpw_rep
.length
);
410 krb5_free_creds(context
, credsp
);
411 krb5_free_principal(context
, creds
.client
);
412 krb5_free_principal(context
, principal
);
413 krb5_free_context(context
);
414 DEBUG(1,("recv of chpw reply failed (%s)\n", strerror(errno
)));
415 return ADS_ERROR_SYSTEM(errno
);
419 chpw_rep
.length
= ret
;
421 ret
= krb5_auth_con_setaddrs(context
, auth_context
, NULL
,&remote_kaddr
);
425 krb5_free_creds(context
, credsp
);
426 krb5_free_principal(context
, creds
.client
);
427 krb5_free_principal(context
, principal
);
428 krb5_free_context(context
);
429 DEBUG(1,("krb5_auth_con_setaddrs on reply failed (%s)\n",
430 error_message(ret
)));
431 return ADS_ERROR_KRB5(ret
);
434 ret
= parse_setpw_reply(context
, auth_context
, &chpw_rep
);
439 krb5_free_creds(context
, credsp
);
440 krb5_free_principal(context
, creds
.client
);
441 krb5_free_principal(context
, principal
);
442 krb5_free_context(context
);
443 DEBUG(1,("parse_setpw_reply failed (%s)\n",
444 error_message(ret
)));
445 return ADS_ERROR_KRB5(ret
);
449 krb5_free_creds(context
, credsp
);
450 krb5_free_principal(context
, creds
.client
);
451 krb5_free_principal(context
, principal
);
452 krb5_free_context(context
);
458 ADS_STATUS
kerberos_set_password(const char *kpasswd_server
,
459 const char *auth_principal
, const char *auth_password
,
460 const char *target_principal
, const char *new_password
,
465 if ((ret
= kerberos_kinit_password(auth_principal
, auth_password
, time_offset
))) {
466 DEBUG(1,("Failed kinit for principal %s (%s)\n", auth_principal
, error_message(ret
)));
467 return ADS_ERROR_KRB5(ret
);
470 return krb5_set_password(kpasswd_server
, target_principal
, new_password
, time_offset
);