2 * Copyright (c) 1997 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. All advertising materials mentioning features or use of this software
18 * must display the following acknowledgement:
19 * This product includes software developed by Kungliga Tekniska
20 * Högskolan and its contributors.
22 * 4. Neither the name of the Institute nor the names of its contributors
23 * may be used to endorse or promote products derived from this software
24 * without specific prior written permission.
26 * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND
27 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
28 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
29 * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE
30 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
31 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
32 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
33 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
34 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
35 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 #include <krb5_locl.h>
43 static krb5_error_code
44 get_kdc_address (krb5_context context
,
46 struct sockaddr_in
*addr
)
49 struct hostent
*hostent
;
53 ret
= krb5_get_krbhst (context
,
59 dot
= strchr (*hostlist
, ':');
63 hostent
= gethostbyname (*hostlist
);
64 krb5_free_krbhst (context
, hostlist
);
66 return h_errno
; /* XXX */
68 memset (addr
, 0, sizeof(*addr
));
69 addr
->sin_family
= AF_INET
;
70 memcpy (&addr
->sin_addr
, hostent
->h_addr_list
[0], sizeof(addr
->sin_addr
));
71 addr
->sin_port
= krb5_getportbyname (context
, "kpasswd", "udp",
77 static krb5_error_code
78 send_request (krb5_context context
,
79 krb5_auth_context
*auth_context
,
82 struct sockaddr_in addr
,
86 krb5_data ap_req_data
;
87 krb5_data krb_priv_data
;
88 krb5_data passwd_data
;
95 krb5_data_zero (&ap_req_data
);
97 ret
= krb5_mk_req_extended (context
,
99 AP_OPTS_MUTUAL_REQUIRED
,
106 passwd_data
.data
= passwd
;
107 passwd_data
.length
= strlen(passwd
);
109 krb5_data_zero (&krb_priv_data
);
111 ret
= krb5_mk_priv (context
,
119 len
= 6 + ap_req_data
.length
+ krb_priv_data
.length
;
121 *p
++ = (len
>> 8) & 0xFF;
122 *p
++ = (len
>> 0) & 0xFF;
125 *p
++ = (ap_req_data
.length
>> 8) & 0xFF;
126 *p
++ = (ap_req_data
.length
>> 0) & 0xFF;
128 memset(&msghdr
, 0, sizeof(msghdr
));
129 msghdr
.msg_name
= (void *)&addr
;
130 msghdr
.msg_namelen
= sizeof(addr
);
131 msghdr
.msg_iov
= iov
;
132 msghdr
.msg_iovlen
= sizeof(iov
)/sizeof(*iov
);
134 msghdr
.msg_control
= NULL
;
135 msghdr
.msg_controllen
= 0;
138 iov
[0].iov_base
= (void*)header
;
140 iov
[1].iov_base
= ap_req_data
.data
;
141 iov
[1].iov_len
= ap_req_data
.length
;
142 iov
[2].iov_base
= krb_priv_data
.data
;
143 iov
[2].iov_len
= krb_priv_data
.length
;
145 if (sendmsg (sock
, &msghdr
, 0) < 0)
148 krb5_data_free (&ap_req_data
);
149 krb5_data_free (&krb_priv_data
);
154 str2data (krb5_data
*d
,
163 d
->length
= vasprintf ((char **)&d
->data
, fmt
, args
);
167 static krb5_error_code
168 process_reply (krb5_context context
,
169 krb5_auth_context auth_context
,
172 krb5_data
*result_code_string
,
173 krb5_data
*result_string
)
176 u_char reply
[BUFSIZ
];
178 u_int16_t pkt_len
, pkt_ver
;
179 krb5_data ap_rep_data
;
181 ret
= recvfrom (sock
, reply
, sizeof(reply
), 0, NULL
, NULL
);
186 pkt_len
= (reply
[0] << 8) | (reply
[1]);
187 pkt_ver
= (reply
[2] << 8) | (reply
[3]);
189 if (pkt_len
!= len
) {
190 str2data (result_string
, "client: wrong len in reply");
191 *result_code
= KRB5_KPASSWD_MALFORMED
;
194 if (pkt_ver
!= 0x0001) {
195 str2data (result_string
,
196 "client: wrong version number (%d)", pkt_ver
);
197 *result_code
= KRB5_KPASSWD_MALFORMED
;
201 ap_rep_data
.data
= reply
+ 6;
202 ap_rep_data
.length
= (reply
[4] << 8) | (reply
[5]);
204 if (ap_rep_data
.length
) {
205 krb5_ap_rep_enc_part
*ap_rep
;
209 ret
= krb5_rd_rep (context
,
216 krb5_free_ap_rep_enc_part (context
, ap_rep
);
218 priv_data
.data
= (u_char
*)ap_rep_data
.data
+ ap_rep_data
.length
;
219 priv_data
.length
= len
- ap_rep_data
.length
- 6;
221 ret
= krb5_rd_priv (context
,
227 krb5_data_free (result_code_string
);
231 if (result_code_string
->length
< 2) {
232 *result_code
= KRB5_KPASSWD_MALFORMED
;
233 str2data (result_string
,
234 "client: bad length in result");
237 p
= result_code_string
->data
;
239 *result_code
= (p
[0] << 8) | p
[1];
240 krb5_data_copy (result_string
,
241 (unsigned char*)result_code_string
->data
+ 2,
242 result_code_string
->length
- 2);
249 ret
= decode_KRB_ERROR(reply
+ 6, len
- 6, &error
, &size
);
253 if (error
.e_data
->length
< 2) {
254 krb5_warnx (context
, "too short e_data to print anything usable");
258 p
= error
.e_data
->data
;
259 *result_code
= (p
[0] << 8) | p
[1];
260 krb5_data_copy (result_string
,
262 error
.e_data
->length
- 2);
268 krb5_change_password (krb5_context context
,
272 krb5_data
*result_code_string
,
273 krb5_data
*result_string
)
276 krb5_auth_context auth_context
= NULL
;
279 struct sockaddr_in addr
;
282 sock
= socket (AF_INET
, SOCK_DGRAM
, 0);
286 ret
= get_kdc_address (context
, creds
->client
->realm
, &addr
);
290 ret
= krb5_auth_con_init (context
, &auth_context
);
294 krb5_auth_con_setflags (context
, auth_context
,
295 KRB5_AUTH_CONTEXT_DO_SEQUENCE
);
297 for (i
= 0; i
< 5; ++i
) {
301 ret
= send_request (context
,
311 FD_SET(sock
, &fdset
);
315 ret
= select (sock
+ 1, &fdset
, NULL
, NULL
, &tv
);
316 if (ret
< 0 && errno
!= EINTR
)
322 ret
= KRB5_KDC_UNREACH
;
326 ret
= process_reply (context
,
334 krb5_auth_con_free (context
, auth_context
);