use new addr_families functions. Now works over IPv6
[heimdal.git] / lib / krb5 / changepw.c
blob7864444949ea6944b37fee17c734a4814690770d
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 = 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 return ret;
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 return errno;
160 krb5_data_free (&ap_req_data);
161 krb5_data_free (&krb_priv_data);
162 return 0;
165 static void
166 str2data (krb5_data *d,
167 char *fmt,
168 ...)
170 char *p;
171 size_t len;
172 va_list args;
174 va_start(args, fmt);
175 d->length = vasprintf ((char **)&d->data, fmt, args);
176 va_end(args);
179 static krb5_error_code
180 process_reply (krb5_context context,
181 krb5_auth_context auth_context,
182 int sock,
183 int *result_code,
184 krb5_data *result_code_string,
185 krb5_data *result_string)
187 krb5_error_code ret;
188 u_char reply[BUFSIZ];
189 size_t len;
190 u_int16_t pkt_len, pkt_ver;
191 krb5_data ap_rep_data;
193 ret = recvfrom (sock, reply, sizeof(reply), 0, NULL, NULL);
194 if (ret < 0)
195 return errno;
197 len = ret;
198 pkt_len = (reply[0] << 8) | (reply[1]);
199 pkt_ver = (reply[2] << 8) | (reply[3]);
201 if (pkt_len != len) {
202 str2data (result_string, "client: wrong len in reply");
203 *result_code = KRB5_KPASSWD_MALFORMED;
204 return 0;
206 if (pkt_ver != 0x0001) {
207 str2data (result_string,
208 "client: wrong version number (%d)", pkt_ver);
209 *result_code = KRB5_KPASSWD_MALFORMED;
210 return 0;
213 ap_rep_data.data = reply + 6;
214 ap_rep_data.length = (reply[4] << 8) | (reply[5]);
216 if (ap_rep_data.length) {
217 krb5_ap_rep_enc_part *ap_rep;
218 krb5_data priv_data;
219 u_char *p;
221 ret = krb5_rd_rep (context,
222 auth_context,
223 &ap_rep_data,
224 &ap_rep);
225 if (ret)
226 return ret;
228 krb5_free_ap_rep_enc_part (context, ap_rep);
230 priv_data.data = (u_char*)ap_rep_data.data + ap_rep_data.length;
231 priv_data.length = len - ap_rep_data.length - 6;
233 ret = krb5_rd_priv (context,
234 auth_context,
235 &priv_data,
236 result_code_string,
237 NULL);
238 if (ret) {
239 krb5_data_free (result_code_string);
240 return ret;
243 if (result_code_string->length < 2) {
244 *result_code = KRB5_KPASSWD_MALFORMED;
245 str2data (result_string,
246 "client: bad length in result");
247 return 0;
249 p = result_code_string->data;
251 *result_code = (p[0] << 8) | p[1];
252 krb5_data_copy (result_string,
253 (unsigned char*)result_code_string->data + 2,
254 result_code_string->length - 2);
255 return 0;
256 } else {
257 KRB_ERROR error;
258 size_t size;
259 u_char *p;
261 ret = decode_KRB_ERROR(reply + 6, len - 6, &error, &size);
262 if (ret) {
263 return ret;
265 if (error.e_data->length < 2) {
266 krb5_warnx (context, "too short e_data to print anything usable");
267 return 1;
270 p = error.e_data->data;
271 *result_code = (p[0] << 8) | p[1];
272 krb5_data_copy (result_string,
273 p + 2,
274 error.e_data->length - 2);
275 return 0;
279 krb5_error_code
280 krb5_change_password (krb5_context context,
281 krb5_creds *creds,
282 char *newpw,
283 int *result_code,
284 krb5_data *result_code_string,
285 krb5_data *result_string)
287 krb5_error_code ret;
288 krb5_auth_context auth_context = NULL;
289 krb5_creds cred;
290 int sock;
291 int i;
292 char *buf;
293 struct sockaddr *sa;
294 int sa_size;
296 ret = krb5_auth_con_init (context, &auth_context);
297 if (ret)
298 return ret;
300 buf = malloc (krb5_max_sockaddr_size ());
301 if (buf == NULL) {
302 ret = ENOMEM;
303 goto out;
305 sa = (struct sockaddr *)buf;
307 ret = get_kdc_address (context, creds->client->realm, sa, &sa_size);
308 if (ret)
309 goto out;
311 sock = socket (sa->sa_family, SOCK_DGRAM, 0);
312 if (sock < 0) {
313 ret = errno;
314 goto out;
317 krb5_auth_con_setflags (context, auth_context,
318 KRB5_AUTH_CONTEXT_DO_SEQUENCE);
320 for (i = 0; i < 5; ++i) {
321 struct fd_set fdset;
322 struct timeval tv;
324 ret = send_request (context,
325 &auth_context,
326 creds,
327 sock,
329 sa_size,
330 newpw);
331 if (ret)
332 goto out;
334 FD_ZERO(&fdset);
335 FD_SET(sock, &fdset);
336 tv.tv_usec = 0;
337 tv.tv_sec = 1 << i;
339 ret = select (sock + 1, &fdset, NULL, NULL, &tv);
340 if (ret < 0 && errno != EINTR)
341 goto out;
342 if (ret == 1)
343 break;
345 if (i == 5) {
346 ret = KRB5_KDC_UNREACH;
347 goto out;
350 ret = process_reply (context,
351 auth_context,
352 sock,
353 result_code,
354 result_code_string,
355 result_string);
357 out:
358 krb5_auth_con_free (context, auth_context);
359 free (buf);
360 return ret;