2 * Copyright (c) 1997 - 2002 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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
34 #include <krb5_locl.h>
38 static krb5_error_code
39 send_request (krb5_context context
,
40 krb5_auth_context
*auth_context
,
47 krb5_data ap_req_data
;
48 krb5_data krb_priv_data
;
49 krb5_data passwd_data
;
56 krb5_data_zero (&ap_req_data
);
58 ret
= krb5_mk_req_extended (context
,
60 AP_OPTS_MUTUAL_REQUIRED
| AP_OPTS_USE_SUBKEY
,
67 passwd_data
.data
= passwd
;
68 passwd_data
.length
= strlen(passwd
);
70 krb5_data_zero (&krb_priv_data
);
72 ret
= krb5_mk_priv (context
,
80 len
= 6 + ap_req_data
.length
+ krb_priv_data
.length
;
82 *p
++ = (len
>> 8) & 0xFF;
83 *p
++ = (len
>> 0) & 0xFF;
86 *p
++ = (ap_req_data
.length
>> 8) & 0xFF;
87 *p
++ = (ap_req_data
.length
>> 0) & 0xFF;
89 memset(&msghdr
, 0, sizeof(msghdr
));
90 msghdr
.msg_name
= NULL
;
91 msghdr
.msg_namelen
= 0;
93 msghdr
.msg_iovlen
= sizeof(iov
)/sizeof(*iov
);
95 msghdr
.msg_control
= NULL
;
96 msghdr
.msg_controllen
= 0;
99 iov
[0].iov_base
= (void*)header
;
101 iov
[1].iov_base
= ap_req_data
.data
;
102 iov
[1].iov_len
= ap_req_data
.length
;
103 iov
[2].iov_base
= krb_priv_data
.data
;
104 iov
[2].iov_len
= krb_priv_data
.length
;
106 if (sendmsg (sock
, &msghdr
, 0) < 0) {
108 krb5_set_error_string(context
, "sendmsg %s: %s", host
, strerror(ret
));
111 krb5_data_free (&krb_priv_data
);
113 krb5_data_free (&ap_req_data
);
118 str2data (krb5_data
*d
,
120 ...) __attribute__ ((format (printf
, 2, 3)));
123 str2data (krb5_data
*d
,
130 d
->length
= vasprintf ((char **)&d
->data
, fmt
, args
);
134 static krb5_error_code
135 process_reply (krb5_context context
,
136 krb5_auth_context auth_context
,
139 krb5_data
*result_code_string
,
140 krb5_data
*result_string
,
144 u_char reply
[BUFSIZ
];
146 u_int16_t pkt_len
, pkt_ver
;
147 krb5_data ap_rep_data
, priv_data
;
150 ret
= recvfrom (sock
, reply
, sizeof(reply
), 0, NULL
, NULL
);
153 krb5_set_error_string(context
, "recvfrom %s: %s",
154 host
, strerror(save_errno
));
159 pkt_len
= (reply
[0] << 8) | (reply
[1]);
160 pkt_ver
= (reply
[2] << 8) | (reply
[3]);
162 if (pkt_len
!= len
) {
163 str2data (result_string
, "client: wrong len in reply");
164 *result_code
= KRB5_KPASSWD_MALFORMED
;
167 if (pkt_ver
!= 0x0001) {
168 str2data (result_string
,
169 "client: wrong version number (%d)", pkt_ver
);
170 *result_code
= KRB5_KPASSWD_MALFORMED
;
174 ap_rep_data
.data
= reply
+ 6;
175 ap_rep_data
.length
= (reply
[4] << 8) | (reply
[5]);
176 priv_data
.data
= (u_char
*)ap_rep_data
.data
+ ap_rep_data
.length
;
177 priv_data
.length
= len
- ap_rep_data
.length
- 6;
178 if ((u_char
*)priv_data
.data
+ priv_data
.length
> reply
+ len
)
179 return KRB5_KPASSWD_MALFORMED
;
181 if (ap_rep_data
.length
) {
182 krb5_ap_rep_enc_part
*ap_rep
;
185 ret
= krb5_rd_rep (context
,
192 krb5_free_ap_rep_enc_part (context
, ap_rep
);
194 ret
= krb5_rd_priv (context
,
200 krb5_data_free (result_code_string
);
204 if (result_code_string
->length
< 2) {
205 *result_code
= KRB5_KPASSWD_MALFORMED
;
206 str2data (result_string
,
207 "client: bad length in result");
210 p
= result_code_string
->data
;
212 *result_code
= (p
[0] << 8) | p
[1];
213 krb5_data_copy (result_string
,
214 (unsigned char*)result_code_string
->data
+ 2,
215 result_code_string
->length
- 2);
222 ret
= decode_KRB_ERROR(reply
+ 6, len
- 6, &error
, &size
);
226 if (error
.e_data
->length
< 2) {
227 krb5_warnx (context
, "too short e_data to print anything usable");
231 p
= error
.e_data
->data
;
232 *result_code
= (p
[0] << 8) | p
[1];
233 krb5_data_copy (result_string
,
235 error
.e_data
->length
- 2);
241 * change the password using the credentials in `creds' (for the
242 * principal indicated in them) to `newpw', storing the result of
243 * the operation in `result_*' and an error code or 0.
247 krb5_change_password (krb5_context context
,
251 krb5_data
*result_code_string
,
252 krb5_data
*result_string
)
255 krb5_auth_context auth_context
= NULL
;
256 krb5_krbhst_handle handle
= NULL
;
257 krb5_krbhst_info
*hi
;
261 krb5_realm realm
= creds
->client
->realm
;
263 ret
= krb5_auth_con_init (context
, &auth_context
);
267 krb5_auth_con_setflags (context
, auth_context
,
268 KRB5_AUTH_CONTEXT_DO_SEQUENCE
);
270 ret
= krb5_krbhst_init (context
, realm
, KRB5_KRBHST_CHANGEPW
, &handle
);
274 while (!done
&& (ret
= krb5_krbhst_next(context
, handle
, &hi
)) == 0) {
275 struct addrinfo
*ai
, *a
;
277 ret
= krb5_krbhst_get_addrinfo(context
, hi
, &ai
);
281 for (a
= ai
; !done
&& a
!= NULL
; a
= a
->ai_next
) {
284 sock
= socket (a
->ai_family
, a
->ai_socktype
, a
->ai_protocol
);
288 ret
= connect(sock
, a
->ai_addr
, a
->ai_addrlen
);
294 ret
= krb5_auth_con_genaddrs (context
, auth_context
, sock
,
295 KRB5_AUTH_CONTEXT_GENERATE_LOCAL_ADDR
);
301 for (i
= 0; !done
&& i
< 5; ++i
) {
307 ret
= send_request (context
,
319 if (sock
>= FD_SETSIZE
) {
320 krb5_set_error_string(context
, "fd %d too large", sock
);
327 FD_SET(sock
, &fdset
);
329 tv
.tv_sec
= 1 + (1 << i
);
331 ret
= select (sock
+ 1, &fdset
, NULL
, NULL
, &tv
);
332 if (ret
< 0 && errno
!= EINTR
) {
337 ret
= process_reply (context
,
346 else if (i
> 0 && ret
== KRB5KRB_AP_ERR_MUT_FAIL
)
349 ret
= KRB5_KDC_UNREACH
;
357 krb5_krbhst_free (context
, handle
);
358 krb5_auth_con_free (context
, auth_context
);
362 if (ret
== KRB5_KDC_UNREACH
)
363 krb5_set_error_string(context
,
364 "unable to reach any changepw server "
365 " in realm %s", realm
);
371 krb5_passwd_result_to_string (krb5_context context
,
374 static const char *strings
[] = {
382 if (result
< 0 || result
> KRB5_KPASSWD_SOFTERROR
)
383 return "unknown result code";
385 return strings
[result
];