2 * lib/krb5/os/changepw.c
4 * Copyright 1990 by the Massachusetts Institute of Technology.
7 * Export of this software from the United States of America may
8 * require a specific license from the United States Government.
9 * It is the responsibility of any person or organization contemplating
10 * export to obtain such a license before exporting.
12 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13 * distribute this software and its documentation for any purpose and
14 * without fee is hereby granted, provided that the above copyright
15 * notice appear in all copies and that both that copyright notice and
16 * this permission notice appear in supporting documentation, and that
17 * the name of M.I.T. not be used in advertising or publicity pertaining
18 * to distribution of the software without specific, written prior
19 * permission. M.I.T. makes no representations about the suitability of
20 * this software for any purpose. It is provided "as is" without express
21 * or implied warranty.
33 #if defined(_WIN16) || (defined(_WIN32) && !defined(__CYGWIN32__))
35 #define ECONNABORTED WSAECONNABORTED
38 #define ECONNREFUSED WSAECONNREFUSED
41 #define EHOSTUNREACH WSAEHOSTUNREACH
43 #endif /* _WIN32 && !__CYGWIN32__ */
45 KRB5_DLLIMP krb5_error_code KRB5_CALLCONV
46 krb5_change_password(context, creds, newpw, result_code,
47 result_code_string, result_string)
52 krb5_data *result_code_string;
53 krb5_data *result_string;
55 krb5_auth_context auth_context;
56 krb5_data ap_req, chpw_req, chpw_rep;
57 krb5_address local_kaddr, remote_kaddr;
58 const char *realm_kdc_names[4];
60 char **hostlist, *host, *port, *cp, *code_string;
62 int i, j, out, count, addrlen;
63 struct sockaddr *addr_p, local_addr, remote_addr, tmp_addr;
64 struct sockaddr_in *sin_p;
67 #ifdef HAVE_NETINET_IN_H
68 u_short udpport = htons(KRB5_DEFAULT_PORT);
70 int cc, local_result_code, tmp_len;
75 if (code = krb5_mk_req_extended(context, &auth_context, AP_OPTS_USE_SUBKEY,
76 NULL, creds, &ap_req))
79 if ((host = malloc(krb5_princ_realm(context, creds->client)->length + 1))
83 strncpy(host, krb5_princ_realm(context, creds->client)->data,
84 krb5_princ_realm(context, creds->client)->length);
85 host[krb5_princ_realm(context, creds->client)->length] = '\0';
88 realm_kdc_names[0] = "realms";
89 realm_kdc_names[1] = host;
90 realm_kdc_names[2] = "kpasswd_server";
91 realm_kdc_names[3] = 0;
95 code = profile_get_values(context->profile, realm_kdc_names, &hostlist);
97 if (code == PROF_NO_RELATION) {
98 realm_kdc_names[2] = "admin_server";
102 code = profile_get_values(context->profile, realm_kdc_names,
108 if (code == PROF_NO_SECTION)
109 return KRB5_REALM_UNKNOWN;
110 else if (code == PROF_NO_RELATION)
111 return KRB5_CONFIG_BADFORMAT;
115 #ifdef HAVE_NETINET_IN_H
116 /* XXX should look for "kpasswd" in /etc/services */
117 udpport = htons(DEFAULT_KPASSWD_PORT);
121 while (hostlist && hostlist[count])
126 return(KADM_NO_HOST);
128 addr_p = (struct sockaddr *) malloc(sizeof(struct sockaddr) * count);
136 * Strip off excess whitespace
138 cp = strchr(host, ' ');
141 cp = strchr(host, '\t');
144 port = strchr(host, ':');
148 /* if the admin_server line was used, ignore the specified
153 hp = gethostbyname(hostlist[0]);
156 switch (hp->h_addrtype) {
157 #ifdef HAVE_NETINET_IN_H
159 for (j=0; hp->h_addr_list[j]; j++) {
160 sin_p = (struct sockaddr_in *) &addr_p[out++];
161 memset ((char *)sin_p, 0, sizeof(struct sockaddr));
162 sin_p->sin_family = hp->h_addrtype;
163 sin_p->sin_port = port ? htons(atoi(port)) : udpport;
164 memcpy((char *)&sin_p->sin_addr,
165 (char *)hp->h_addr_list[j],
166 sizeof(struct in_addr));
167 if (out+1 >= count) {
169 addr_p = (struct sockaddr *)
170 realloc ((char *)addr_p,
171 sizeof(struct sockaddr) * count);
183 for (i=0; hostlist[i]; i++)
185 free ((char *)hostlist);
187 if (out == 0) { /* Couldn't resolve any KDC names */
189 return(KADM_NO_HOST);
192 /* this is really obscure. s1 is used for all communications. it
193 is left unconnected in case the server is multihomed and routes
194 are asymmetric. s2 is connected to resolve routes and get
195 addresses. this is the *only* way to get proper addresses for
196 multihomed hosts if routing is asymmetric.
198 A related problem in the server, but not the client, is that
199 many os's have no way to disconnect a connected udp socket, so
200 the s2 socket needs to be closed and recreated for each
201 request. The s1 socket must not be closed, or else queued
202 requests will be lost.
204 A "naive" client implementation (one socket, no connect,
205 hostname resolution to get the local ip addr) will work and
206 interoperate if the client is single-homed. */
208 if ((s1 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
210 return(SOCKET_ERRNO);
213 if ((s2 = socket(AF_INET, SOCK_DGRAM, 0)) == INVALID_SOCKET) {
215 return(SOCKET_ERRNO);
218 for (i=0; i<out; i++) {
219 if (connect(s2, &addr_p[i], sizeof(addr_p[i])) == SOCKET_ERROR) {
220 if ((cc < 0) && ((SOCKET_ERRNO == ECONNREFUSED) ||
221 (SOCKET_ERRNO == EHOSTUNREACH)))
222 continue; /* try the next addr */
226 return(SOCKET_ERRNO);
229 addrlen = sizeof(local_addr);
231 if (getsockname(s2, &local_addr, &addrlen) < 0) {
232 if ((SOCKET_ERRNO == ECONNREFUSED) ||
233 (SOCKET_ERRNO == EHOSTUNREACH))
234 continue; /* try the next addr */
238 return(SOCKET_ERRNO);
241 /* some brain-dead OS's don't return useful information from
242 * the getsockname call. Namely, windows and solaris. */
244 if (((struct sockaddr_in *)&local_addr)->sin_addr.s_addr != 0) {
245 local_kaddr.addrtype = ADDRTYPE_INET;
247 sizeof(((struct sockaddr_in *) &local_addr)->sin_addr);
248 local_kaddr.contents =
249 (krb5_octet *) &(((struct sockaddr_in *) &local_addr)->sin_addr);
251 krb5_address **addrs;
253 krb5_os_localaddr(context, &addrs);
254 local_kaddr.magic = addrs[0]->magic;
255 local_kaddr.addrtype = addrs[0]->addrtype;
256 local_kaddr.length = addrs[0]->length;
257 local_kaddr.contents = malloc(addrs[0]->length);
258 memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length);
260 krb5_free_addresses(context, addrs);
263 addrlen = sizeof(remote_addr);
264 if (getpeername(s2, &remote_addr, &addrlen) < 0) {
265 if ((SOCKET_ERRNO == ECONNREFUSED) ||
266 (SOCKET_ERRNO == EHOSTUNREACH))
267 continue; /* try the next addr */
271 return(SOCKET_ERRNO);
274 remote_kaddr.addrtype = ADDRTYPE_INET;
275 remote_kaddr.length =
276 sizeof(((struct sockaddr_in *) &remote_addr)->sin_addr);
277 remote_kaddr.contents =
278 (krb5_octet *) &(((struct sockaddr_in *) &remote_addr)->sin_addr);
280 /* mk_priv requires that the local address be set.
281 getsockname is used for this. rd_priv requires that the
282 remote address be set. recvfrom is used for this. If
283 rd_priv is given a local address, and the message has the
284 recipient addr in it, this will be checked. However, there
285 is simply no way to know ahead of time what address the
286 message will be delivered *to*. Therefore, it is important
287 that either no recipient address is in the messages when
288 mk_priv is called, or that no local address is passed to
289 rd_priv. Both is a better idea, and I have done that. In
290 summary, when mk_priv is called, *only* a local address is
291 specified. when rd_priv is called, *only* a remote address
292 is specified. Are we having fun yet? */
294 if (code = krb5_auth_con_setaddrs(context, auth_context, &local_kaddr,
302 if (code = krb5_mk_chpw_req(context, auth_context, &ap_req,
310 if ((cc = sendto(s1, chpw_req.data, chpw_req.length, 0,
311 (struct sockaddr *) &addr_p[i],
312 sizeof(addr_p[i]))) !=
314 if ((cc < 0) && ((SOCKET_ERRNO == ECONNREFUSED) ||
315 (SOCKET_ERRNO == EHOSTUNREACH)))
316 continue; /* try the next addr */
320 return((cc < 0)?SOCKET_ERRNO:ECONNABORTED);
323 krb5_xfree(chpw_req.data);
325 chpw_rep.length = 1500;
326 chpw_rep.data = (char *) malloc(chpw_rep.length);
328 /* XXX need a timeout/retry loop here */
330 /* "recv" would be good enough here... except that Windows/NT
331 commits the atrocity of returning -1 to indicate failure,
332 but leaving errno set to 0.
334 "recvfrom(...,NULL,NULL)" would seem to be a good enough
335 alternative, and it works on NT, but it doesn't work on
336 SunOS 4.1.4 or Irix 5.3. Thus we must actually accept the
337 value and discard it. */
338 tmp_len = sizeof(tmp_addr);
339 if ((cc = recvfrom(s1, chpw_rep.data, chpw_rep.length, 0, &tmp_addr, &tmp_len)) < 0) {
343 return(SOCKET_ERRNO);
349 chpw_rep.length = cc;
351 if (code = krb5_auth_con_setaddrs(context, auth_context, NULL,
359 code = krb5_rd_chpw_rep(context, auth_context, &chpw_rep,
360 &local_result_code, result_string);
369 *result_code = local_result_code;
371 if (result_code_string) {
372 if (code = krb5_chpw_result_code_string(context, local_result_code,
376 result_code_string->length = strlen(code_string);
377 if ((result_code_string->data =
378 (char *) malloc(result_code_string->length)) == NULL)
380 strncpy(result_code_string->data, code_string,
381 result_code_string->length);
390 return(SOCKET_ERRNO);