changed `struct fd_set' to `fd_set'
[heimdal.git] / lib / krb5 / changepw.c
blobce747da630a41ff832de9efd82fe6d0684d7440b
1 /*
2 * Copyright (c) 1997 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
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
36 * SUCH DAMAGE.
39 #include <krb5_locl.h>
41 RCSID("$Id$");
43 static krb5_error_code
44 get_kdc_address (krb5_context context,
45 krb5_realm realm,
46 struct sockaddr *sa,
47 int *sa_size)
49 krb5_error_code ret;
50 struct hostent *hostent;
51 char **hostlist;
52 char *dot;
53 char *p;
55 ret = krb5_get_krbhst (context,
56 &realm,
57 &hostlist);
58 if (ret)
59 return ret;
61 p = *hostlist;
63 dot = strchr (p, ':');
64 if (dot)
65 *dot = '\0';
67 #ifdef HAVE_GETHOSTBYNAME2
68 hostent = gethostbyname2 (p, AF_INET6);
69 if (hostent == NULL)
70 hostent = gethostbyname2 (p, AF_INET);
71 #else
72 hostent = roken_gethostbyname (p);
73 #endif
74 krb5_free_krbhst (context, hostlist);
75 if (hostent == NULL)
76 return h_errno; /* XXX */
78 return krb5_h_addr2sockaddr (hostent->h_addrtype,
79 hostent->h_addr_list[0],
80 sa,
81 sa_size,
82 krb5_getportbyname (context,
83 "kpasswd",
84 "udp",
85 KPASSWD_PORT));
88 static krb5_error_code
89 send_request (krb5_context context,
90 krb5_auth_context *auth_context,
91 krb5_creds *creds,
92 int sock,
93 struct sockaddr *sa,
94 int sa_size,
95 char *passwd)
97 krb5_error_code ret;
98 krb5_data ap_req_data;
99 krb5_data krb_priv_data;
100 krb5_data passwd_data;
101 size_t len;
102 u_char header[6];
103 u_char *p;
104 struct iovec iov[3];
105 struct msghdr msghdr;
107 krb5_data_zero (&ap_req_data);
109 ret = krb5_mk_req_extended (context,
110 auth_context,
111 AP_OPTS_MUTUAL_REQUIRED,
112 NULL, /* in_data */
113 creds,
114 &ap_req_data);
115 if (ret)
116 return ret;
118 passwd_data.data = passwd;
119 passwd_data.length = strlen(passwd);
121 krb5_data_zero (&krb_priv_data);
123 ret = krb5_mk_priv (context,
124 *auth_context,
125 &passwd_data,
126 &krb_priv_data,
127 NULL);
128 if (ret)
129 goto out2;
131 len = 6 + ap_req_data.length + krb_priv_data.length;
132 p = header;
133 *p++ = (len >> 8) & 0xFF;
134 *p++ = (len >> 0) & 0xFF;
135 *p++ = 0;
136 *p++ = 1;
137 *p++ = (ap_req_data.length >> 8) & 0xFF;
138 *p++ = (ap_req_data.length >> 0) & 0xFF;
140 memset(&msghdr, 0, sizeof(msghdr));
141 msghdr.msg_name = (void *)sa;
142 msghdr.msg_namelen = sa_size;
143 msghdr.msg_iov = iov;
144 msghdr.msg_iovlen = sizeof(iov)/sizeof(*iov);
145 #if 0
146 msghdr.msg_control = NULL;
147 msghdr.msg_controllen = 0;
148 #endif
150 iov[0].iov_base = (void*)header;
151 iov[0].iov_len = 6;
152 iov[1].iov_base = ap_req_data.data;
153 iov[1].iov_len = ap_req_data.length;
154 iov[2].iov_base = krb_priv_data.data;
155 iov[2].iov_len = krb_priv_data.length;
157 if (sendmsg (sock, &msghdr, 0) < 0)
158 ret = errno;
160 krb5_data_free (&krb_priv_data);
161 out2:
162 krb5_data_free (&ap_req_data);
163 return ret;
166 static void
167 str2data (krb5_data *d,
168 char *fmt,
169 ...)
171 va_list args;
173 va_start(args, fmt);
174 d->length = vasprintf ((char **)&d->data, fmt, args);
175 va_end(args);
178 static krb5_error_code
179 process_reply (krb5_context context,
180 krb5_auth_context auth_context,
181 int sock,
182 int *result_code,
183 krb5_data *result_code_string,
184 krb5_data *result_string)
186 krb5_error_code ret;
187 u_char reply[BUFSIZ];
188 size_t len;
189 u_int16_t pkt_len, pkt_ver;
190 krb5_data ap_rep_data;
192 ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL);
193 if (ret < 0)
194 return errno;
196 len = ret;
197 pkt_len = (reply[0] << 8) | (reply[1]);
198 pkt_ver = (reply[2] << 8) | (reply[3]);
200 if (pkt_len != len) {
201 str2data (result_string, "client: wrong len in reply");
202 *result_code = KRB5_KPASSWD_MALFORMED;
203 return 0;
205 if (pkt_ver != 0x0001) {
206 str2data (result_string,
207 "client: wrong version number (%d)", pkt_ver);
208 *result_code = KRB5_KPASSWD_MALFORMED;
209 return 0;
212 ap_rep_data.data = reply + 6;
213 ap_rep_data.length = (reply[4] << 8) | (reply[5]);
215 if (ap_rep_data.length) {
216 krb5_ap_rep_enc_part *ap_rep;
217 krb5_data priv_data;
218 u_char *p;
220 ret = krb5_rd_rep (context,
221 auth_context,
222 &ap_rep_data,
223 &ap_rep);
224 if (ret)
225 return ret;
227 krb5_free_ap_rep_enc_part (context, ap_rep);
229 priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length;
230 priv_data.length = len - ap_rep_data.length - 6;
232 ret = krb5_rd_priv (context,
233 auth_context,
234 &priv_data,
235 result_code_string,
236 NULL);
237 if (ret) {
238 krb5_data_free (result_code_string);
239 return ret;
242 if (result_code_string->length < 2) {
243 *result_code = KRB5_KPASSWD_MALFORMED;
244 str2data (result_string,
245 "client: bad length in result");
246 return 0;
248 p = result_code_string->data;
250 *result_code = (p[0] << 8) | p[1];
251 krb5_data_copy (result_string,
252 (unsigned char*)result_code_string->data + 2,
253 result_code_string->length - 2);
254 return 0;
255 } else {
256 KRB_ERROR error;
257 size_t size;
258 u_char *p;
260 ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size);
261 if (ret) {
262 return ret;
264 if (error.e_data->length < 2) {
265 krb5_warnx (context, "too short e_data to print anything usable");
266 return 1;
269 p = error.e_data->data;
270 *result_code = (p[0] << 8) | p[1];
271 krb5_data_copy (result_string,
272 p + 2,
273 error.e_data->length - 2);
274 return 0;
278 krb5_error_code
279 krb5_change_password (krb5_context context,
280 krb5_creds *creds,
281 char *newpw,
282 int *result_code,
283 krb5_data *result_code_string,
284 krb5_data *result_string)
286 krb5_error_code ret;
287 krb5_auth_context auth_context = NULL;
288 int sock;
289 int i;
290 char *buf;
291 struct sockaddr *sa;
292 int sa_size;
294 ret = krb5_auth_con_init (context, &auth_context);
295 if (ret)
296 return ret;
298 buf = malloc (krb5_max_sockaddr_size ());
299 if (buf == NULL) {
300 ret = ENOMEM;
301 goto out;
303 sa = (struct sockaddr *)buf;
305 ret = get_kdc_address (context, creds->client->realm, sa, &sa_size);
306 if (ret)
307 goto out;
309 sock = socket (sa->sa_family, SOCK_DGRAM, 0);
310 if (sock < 0) {
311 ret = errno;
312 goto out;
315 krb5_auth_con_setflags (context, auth_context,
316 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
318 for (i = 0; i < 5; ++i) {
319 fd_set fdset;
320 struct timeval tv;
322 ret = send_request (context,
323 &auth_context,
324 creds,
325 sock,
327 sa_size,
328 newpw);
329 if (ret)
330 goto out;
332 FD_ZERO(&fdset);
333 FD_SET(sock, &fdset);
334 tv.tv_usec = 0;
335 tv.tv_sec = 1 << i;
337 ret = select (sock + 1, &fdset, NULL, NULL, &tv);
338 if (ret < 0 && errno != EINTR)
339 goto out;
340 if (ret == 1)
341 break;
343 if (i == 5) {
344 ret = KRB5_KDC_UNREACH;
345 goto out;
348 ret = process_reply (context,
349 auth_context,
350 sock,
351 result_code,
352 result_code_string,
353 result_string);
355 out:
356 krb5_auth_con_free (context, auth_context);
357 free (buf);
358 return ret;