2 * Copyright 2008 Sun Microsystems, Inc. All rights reserved.
3 * Use is subject to license terms.
8 * lib/krb5/os/changepw.c
10 * Copyright 1990,1999 by the Massachusetts Institute of Technology.
11 * All Rights Reserved.
13 * Export of this software from the United States of America may
14 * require a specific license from the United States Government.
15 * It is the responsibility of any person or organization contemplating
16 * export to obtain such a license before exporting.
18 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
19 * distribute this software and its documentation for any purpose and
20 * without fee is hereby granted, provided that the above copyright
21 * notice appear in all copies and that both that copyright notice and
22 * this permission notice appear in supporting documentation, and that
23 * the name of M.I.T. not be used in advertising or publicity pertaining
24 * to distribution of the software without specific, written prior
25 * permission. Furthermore if you modify this software you must label
26 * your software as modified software and not distribute it in such a
27 * fashion that it might be confused with the original M.I.T. software.
28 * M.I.T. makes no representations about the suitability of
29 * this software for any purpose. It is provided "as is" without express
30 * or implied warranty.
36 #include <kadm5/admin.h>
37 #include <client_internal.h>
38 #include <gssapi/gssapi.h>
39 #include <gssapi_krb5.h>
40 #include <gssapiP_krb5.h>
43 /* #include "adm_err.h" */
47 extern krb5_error_code
krb5int_mk_chpw_req(krb5_context context
,
48 krb5_auth_context auth_context
,
49 krb5_data
*ap_req
, char *passwd
,
52 extern krb5_error_code
krb5int_rd_chpw_rep(krb5_context context
,
53 krb5_auth_context auth_context
,
54 krb5_data
*packet
, int *result_code
,
55 krb5_data
*result_data
);
58 * _kadm5_get_kpasswd_protocol
60 * returns the password change protocol value to the caller.
61 * Since the 'handle' is an opaque value to higher up callers,
62 * this method is needed to provide a way for them to get a peek
63 * at the protocol being used without having to expose the entire
67 _kadm5_get_kpasswd_protocol(void *handle
)
69 kadm5_server_handle_t srvrhdl
= (kadm5_server_handle_t
)handle
;
71 return (srvrhdl
->params
.kpasswd_protocol
);
75 * krb5_change_password
77 * Prepare and send a CHANGEPW request to a password server
78 * using UDP datagrams. This is only used for sending to
79 * non-SEAM servers which support the Marc Horowitz defined
80 * protocol (1998) for password changing.
82 * SUNW14resync - added _local as it conflicts with one in krb5.h
84 static krb5_error_code
85 krb5_change_password_local(context
, params
, creds
, newpw
, srvr_rsp_code
,
88 kadm5_config_params
*params
;
91 kadm5_ret_t
*srvr_rsp_code
;
94 krb5_auth_context auth_context
;
95 krb5_data ap_req
, chpw_req
, chpw_rep
;
96 krb5_address local_kaddr
, remote_kaddr
;
97 krb5_error_code code
= 0;
99 socklen_t addrlen
, tmp_len
;
100 struct sockaddr
*addr_p
, local_addr
, remote_addr
, tmp_addr
;
101 struct sockaddr_in
*sin_p
;
104 int cc
, local_result_code
;
105 SOCKET s1
= INVALID_SOCKET
;
106 SOCKET s2
= INVALID_SOCKET
;
109 /* Initialize values so that cleanup call can safely check for NULL */
112 memset(&chpw_req
, 0, sizeof (krb5_data
));
113 memset(&chpw_rep
, 0, sizeof (krb5_data
));
114 memset(&ap_req
, 0, sizeof (krb5_data
));
116 /* initialize auth_context so that we know we have to free it */
117 if ((code
= krb5_auth_con_init(context
, &auth_context
)))
120 if (code
= krb5_mk_req_extended(context
, &auth_context
,
122 NULL
, creds
, &ap_req
))
126 * find the address of the kpasswd_server.
128 addr_p
= (struct sockaddr
*)malloc(sizeof (struct sockaddr
));
131 memset(addr_p
, 0, sizeof (struct sockaddr
));
132 if ((hp
= gethostbyname(params
->kpasswd_server
)) == NULL
) {
133 code
= KRB5_REALM_CANT_RESOLVE
;
136 sin_p
= (struct sockaddr_in
*)addr_p
;
137 memset((char *)sin_p
, 0, sizeof (struct sockaddr
));
138 sin_p
->sin_family
= hp
->h_addrtype
;
139 sin_p
->sin_port
= htons(params
->kpasswd_port
);
140 memcpy((char *)&sin_p
->sin_addr
, (char *)hp
->h_addr
, hp
->h_length
);
145 * this is really obscure. s1 is used for all communications. it
146 * is left unconnected in case the server is multihomed and routes
147 * are asymmetric. s2 is connected to resolve routes and get
148 * addresses. this is the *only* way to get proper addresses for
149 * multihomed hosts if routing is asymmetric.
151 * A related problem in the server, but not the client, is that
152 * many os's have no way to disconnect a connected udp socket, so
153 * the s2 socket needs to be closed and recreated for each
154 * request. The s1 socket must not be closed, or else queued
155 * requests will be lost.
157 * A "naive" client implementation (one socket, no connect,
158 * hostname resolution to get the local ip addr) will work and
159 * interoperate if the client is single-homed.
162 if ((s1
= socket(AF_INET
, SOCK_DGRAM
, 0)) == INVALID_SOCKET
)
168 if ((s2
= socket(AF_INET
, SOCK_DGRAM
, 0)) == INVALID_SOCKET
)
174 for (i
= 0; i
< naddr_p
; i
++)
177 struct timeval timeout
;
179 if (connect(s2
, &addr_p
[i
], sizeof (addr_p
[i
])) ==
182 if ((errno
== ECONNREFUSED
) ||
183 (errno
== EHOSTUNREACH
))
184 continue; /* try the next addr */
190 addrlen
= sizeof (local_addr
);
192 if (getsockname(s2
, &local_addr
, &addrlen
) < 0)
194 if ((errno
== ECONNREFUSED
) ||
195 (errno
== EHOSTUNREACH
))
196 continue; /* try the next addr */
203 * some brain-dead OS's don't return useful information from
204 * the getsockname call. Namely, windows and solaris.
206 if (((struct sockaddr_in
*)&local_addr
)->sin_addr
.s_addr
!= 0)
208 local_kaddr
.addrtype
= ADDRTYPE_INET
;
209 local_kaddr
.length
= sizeof (((struct sockaddr_in
*)
210 &local_addr
)->sin_addr
);
211 local_kaddr
.contents
= (krb5_octet
*)
212 &(((struct sockaddr_in
*)
213 &local_addr
)->sin_addr
);
217 krb5_address
**addrs
;
219 krb5_os_localaddr(context
, &addrs
);
221 local_kaddr
.magic
= addrs
[0]->magic
;
222 local_kaddr
.addrtype
= addrs
[0]->addrtype
;
223 local_kaddr
.length
= addrs
[0]->length
;
224 local_kaddr
.contents
= malloc(addrs
[0]->length
);
225 memcpy(local_kaddr
.contents
, addrs
[0]->contents
,
228 krb5_free_addresses(context
, addrs
);
231 addrlen
= sizeof (remote_addr
);
232 if (getpeername(s2
, &remote_addr
, &addrlen
) < 0)
234 if ((errno
== ECONNREFUSED
) ||
235 (errno
== EHOSTUNREACH
))
236 continue; /* try the next addr */
242 remote_kaddr
.addrtype
= ADDRTYPE_INET
;
243 remote_kaddr
.length
= sizeof (((struct sockaddr_in
*)
244 &remote_addr
)->sin_addr
);
245 remote_kaddr
.contents
= (krb5_octet
*)
246 &(((struct sockaddr_in
*)&remote_addr
)->sin_addr
);
249 * mk_priv requires that the local address be set.
250 * getsockname is used for this. rd_priv requires that the
251 * remote address be set. recvfrom is used for this. If
252 * rd_priv is given a local address, and the message has the
253 * recipient addr in it, this will be checked. However, there
254 * is simply no way to know ahead of time what address the
255 * message will be delivered *to*. Therefore, it is important
256 * that either no recipient address is in the messages when
257 * mk_priv is called, or that no local address is passed to
258 * rd_priv. Both is a better idea, and I have done that. In
259 * summary, when mk_priv is called, *only* a local address is
260 * specified. when rd_priv is called, *only* a remote address
261 * is specified. Are we having fun yet?
264 if (code
= krb5_auth_con_setaddrs(context
, auth_context
,
271 if (code
= krb5int_mk_chpw_req(context
, auth_context
,
272 &ap_req
, newpw
, &chpw_req
))
278 if ((cc
= sendto(s1
, chpw_req
.data
, chpw_req
.length
, 0,
279 (struct sockaddr
*)&addr_p
[i
],
280 sizeof (addr_p
[i
]))) != chpw_req
.length
)
282 if ((cc
< 0) && ((errno
== ECONNREFUSED
) ||
283 (errno
== EHOSTUNREACH
)))
284 continue; /* try the next addr */
286 code
= (cc
< 0) ? errno
: ECONNABORTED
;
290 chpw_rep
.length
= 1500;
291 chpw_rep
.data
= (char *)malloc(chpw_rep
.length
);
293 /* XXX need a timeout/retry loop here */
296 timeout
.tv_sec
= 120;
298 switch (select(s1
+ 1, &fdset
, 0, 0, &timeout
)) {
310 tmp_len
= sizeof (tmp_addr
);
311 if ((cc
= recvfrom(s1
, chpw_rep
.data
, chpw_rep
.length
,
312 0, &tmp_addr
, &tmp_len
)) < 0)
323 chpw_rep
.length
= cc
;
325 if (code
= krb5_auth_con_setaddrs(context
, auth_context
,
326 NULL
, &remote_kaddr
))
329 if (code
= krb5int_rd_chpw_rep(context
, auth_context
, &chpw_rep
,
330 &local_result_code
, srvr_msg
))
334 *srvr_rsp_code
= local_result_code
;
343 if (auth_context
!= NULL
)
344 krb5_auth_con_free(context
, auth_context
);
349 if (s1
!= INVALID_SOCKET
)
352 if (s2
!= INVALID_SOCKET
)
355 krb5_xfree(chpw_req
.data
);
356 krb5_xfree(chpw_rep
.data
);
357 krb5_xfree(ap_req
.data
);
364 * kadm5_chpass_principal_v2
366 * New function used to prepare to make the change password request to a
367 * non-SEAM admin server. The protocol used in this case is not based on
368 * RPCSEC_GSS, it simply makes the request to port 464 (udp and tcp).
369 * This is the same way that MIT KRB5 1.2.1 changes passwords.
372 kadm5_chpass_principal_v2(void *server_handle
,
373 krb5_principal princ
,
375 kadm5_ret_t
*srvr_rsp_code
,
379 kadm5_server_handle_t handle
= (kadm5_server_handle_t
)server_handle
;
380 krb5_error_code result
;
385 char *cpw_service
= NULL
;
388 * The credentials have already been stored in the cache in the
389 * initialization step earlier, but we dont have direct access to it
390 * at this level. Derive the cache and fetch the credentials to use for
391 * sending the request.
393 memset(&mcreds
, 0, sizeof (krb5_creds
));
394 if ((code
= krb5_cc_resolve(handle
->context
, handle
->cache_name
,
398 /* set the client principal in the credential match structure */
399 mcreds
.client
= princ
;
402 * set the server principal (kadmin/changepw@REALM) in the credential
405 cpwlen
= strlen(KADM5_CHANGEPW_SERVICE
) +
406 strlen(handle
->params
.realm
) + 2;
407 cpw_service
= malloc(cpwlen
);
408 if (cpw_service
== NULL
) {
412 snprintf(cpw_service
, cpwlen
, "%s@%s",
413 KADM5_CHANGEPW_SERVICE
, handle
->params
.realm
);
415 /* generate the server principal from the name string we generated */
416 if ((code
= krb5_parse_name(handle
->context
, cpw_service
,
422 /* Find the credentials in the cache */
423 if ((code
= krb5_cc_retrieve_cred(handle
->context
, ccache
, 0, &mcreds
,
429 /* Now we have all we need to make the change request. */
430 result
= krb5_change_password_local(handle
->context
, &handle
->params
,