2 * lib/krb5/os/changepw.c
4 * Copyright 1990,1999,2001 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. Furthermore if you modify this software you must label
20 * your software as modified software and not distribute it in such a
21 * fashion that it might be confused with the original M.I.T. software.
22 * M.I.T. makes no representations about the suitability of
23 * this software for any purpose. It is provided "as is" without express
24 * or implied warranty.
28 * krb5_set_password - Implements set password per RFC 3244
29 * Added by Paul W. Nelson, Thursby Software Systems, Inc.
30 * Modified by Todd Stecher, Isilon Systems, to use krb1.4 socket infrastructure
33 #include "fake-addrinfo.h"
41 #ifndef GETSOCKNAME_ARG3_TYPE
42 #define GETSOCKNAME_ARG3_TYPE int
45 struct sendto_callback_context
{
47 krb5_auth_context auth_context
;
48 krb5_principal set_password_for
;
55 * Wrapper function for the two backends
58 static krb5_error_code
59 krb5_locate_kpasswd(krb5_context context
, const krb5_data
*realm
,
60 struct addrlist
*addrlist
, krb5_boolean useTcp
)
63 int sockType
= (useTcp
? SOCK_STREAM
: SOCK_DGRAM
);
65 code
= krb5int_locate_server (context
, realm
, addrlist
,
66 locate_service_kpasswd
, sockType
, 0);
68 if (code
== KRB5_REALM_CANT_RESOLVE
|| code
== KRB5_REALM_UNKNOWN
) {
69 code
= krb5int_locate_server (context
, realm
, addrlist
,
70 locate_service_kadmin
, SOCK_STREAM
, 0);
72 /* Success with admin_server but now we need to change the
73 port number to use DEFAULT_KPASSWD_PORT and the socktype. */
75 for (i
=0; i
<addrlist
->naddrs
; i
++) {
76 struct addrinfo
*a
= addrlist
->addrs
[i
].ai
;
77 if (a
->ai_family
== AF_INET
)
78 sa2sin (a
->ai_addr
)->sin_port
= htons(DEFAULT_KPASSWD_PORT
);
79 if (sockType
!= SOCK_STREAM
)
80 a
->ai_socktype
= sockType
;
89 * This routine is used for a callback in sendto_kdc.c code. Simply
90 * put, we need the client addr to build the krb_priv portion of the
95 static void kpasswd_sendto_msg_cleanup (void* callback_context
, krb5_data
* message
)
97 struct sendto_callback_context
*ctx
= callback_context
;
98 krb5_free_data_contents(ctx
->context
, message
);
102 static int kpasswd_sendto_msg_callback(struct conn_state
*conn
, void *callback_context
, krb5_data
* message
)
104 krb5_error_code code
= 0;
105 struct sockaddr_storage local_addr
;
106 krb5_address local_kaddr
;
107 struct sendto_callback_context
*ctx
= callback_context
;
108 GETSOCKNAME_ARG3_TYPE addrlen
;
111 memset (message
, 0, sizeof(krb5_data
));
114 * We need the local addr from the connection socket
116 addrlen
= sizeof(local_addr
);
118 if (getsockname(conn
->fd
, ss2sa(&local_addr
), &addrlen
) < 0) {
123 /* some brain-dead OS's don't return useful information from
124 * the getsockname call. Namely, windows and solaris. */
126 if (ss2sin(&local_addr
)->sin_addr
.s_addr
!= 0) {
127 local_kaddr
.addrtype
= ADDRTYPE_INET
;
128 local_kaddr
.length
= sizeof(ss2sin(&local_addr
)->sin_addr
);
129 local_kaddr
.contents
= (krb5_octet
*) &ss2sin(&local_addr
)->sin_addr
;
131 krb5_address
**addrs
;
133 code
= krb5_os_localaddr(ctx
->context
, &addrs
);
137 local_kaddr
.magic
= addrs
[0]->magic
;
138 local_kaddr
.addrtype
= addrs
[0]->addrtype
;
139 local_kaddr
.length
= addrs
[0]->length
;
140 local_kaddr
.contents
= malloc(addrs
[0]->length
);
141 if (local_kaddr
.contents
== NULL
&& addrs
[0]->length
!= 0) {
143 krb5_free_addresses(ctx
->context
, addrs
);
146 memcpy(local_kaddr
.contents
, addrs
[0]->contents
, addrs
[0]->length
);
148 krb5_free_addresses(ctx
->context
, addrs
);
153 * TBD: Does this tamper w/ the auth context in such a way
154 * to break us? Yes - provide 1 per conn-state / host...
158 if ((code
= krb5_auth_con_setaddrs(ctx
->context
, ctx
->auth_context
,
159 &local_kaddr
, NULL
)))
162 if (ctx
->set_password_for
)
163 code
= krb5int_mk_setpw_req(ctx
->context
,
166 ctx
->set_password_for
,
170 code
= krb5int_mk_chpw_req(ctx
->context
,
178 message
->length
= output
.length
;
179 message
->data
= output
.data
;
187 ** The logic for setting and changing a password is mostly the same
188 ** krb5_change_set_password handles both cases
189 ** if set_password_for is NULL, then a password change is performed,
190 ** otherwise, the password is set for the principal indicated in set_password_for
192 krb5_error_code KRB5_CALLCONV
193 krb5_change_set_password(krb5_context context
, krb5_creds
*creds
, char *newpw
,
194 krb5_principal set_password_for
,
195 int *result_code
, krb5_data
*result_code_string
,
196 krb5_data
*result_string
)
199 krb5_address remote_kaddr
;
200 krb5_boolean useTcp
= 0;
201 GETSOCKNAME_ARG3_TYPE addrlen
;
202 krb5_error_code code
= 0;
204 int local_result_code
;
206 struct sendto_callback_context callback_ctx
;
207 struct sendto_callback_info callback_info
;
208 struct sockaddr_storage remote_addr
;
209 struct addrlist al
= ADDRLIST_INIT
;
211 memset( &callback_ctx
, 0, sizeof(struct sendto_callback_context
));
212 callback_ctx
.context
= context
;
213 callback_ctx
.newpw
= newpw
;
214 callback_ctx
.set_password_for
= set_password_for
;
216 if ((code
= krb5_auth_con_init(callback_ctx
.context
,
217 &callback_ctx
.auth_context
)))
220 if ((code
= krb5_mk_req_extended(callback_ctx
.context
,
221 &callback_ctx
.auth_context
,
225 &callback_ctx
.ap_req
)))
229 if ((code
= krb5_locate_kpasswd(callback_ctx
.context
,
230 krb5_princ_realm(callback_ctx
.context
,
235 addrlen
= sizeof(remote_addr
);
237 callback_info
.context
= (void*) &callback_ctx
;
238 callback_info
.pfn_callback
= kpasswd_sendto_msg_callback
;
239 callback_info
.pfn_cleanup
= kpasswd_sendto_msg_cleanup
;
241 if ((code
= krb5int_sendto(callback_ctx
.context
,
256 * Here we may want to switch to TCP on some errors.
262 remote_kaddr
.addrtype
= ADDRTYPE_INET
;
263 remote_kaddr
.length
= sizeof(ss2sin(&remote_addr
)->sin_addr
);
264 remote_kaddr
.contents
= (krb5_octet
*) &ss2sin(&remote_addr
)->sin_addr
;
266 if ((code
= krb5_auth_con_setaddrs(callback_ctx
.context
,
267 callback_ctx
.auth_context
,
272 if (set_password_for
)
273 code
= krb5int_rd_setpw_rep(callback_ctx
.context
,
274 callback_ctx
.auth_context
,
279 code
= krb5int_rd_chpw_rep(callback_ctx
.context
,
280 callback_ctx
.auth_context
,
286 if (code
== KRB5KRB_ERR_RESPONSE_TOO_BIG
&& !useTcp
) {
287 krb5int_free_addrlist (&al
);
296 *result_code
= local_result_code
;
298 if (result_code_string
) {
299 if (set_password_for
)
300 code
= krb5int_setpw_result_code_string(callback_ctx
.context
,
302 (const char **)&code_string
);
304 code
= krb5_chpw_result_code_string(callback_ctx
.context
,
310 result_code_string
->length
= strlen(code_string
);
311 result_code_string
->data
= malloc(result_code_string
->length
);
312 if (result_code_string
->data
== NULL
) {
316 strncpy(result_code_string
->data
, code_string
, result_code_string
->length
);
319 if (code
== KRB5KRB_ERR_RESPONSE_TOO_BIG
&& !useTcp
) {
320 krb5int_free_addrlist (&al
);
328 if (callback_ctx
.auth_context
!= NULL
)
329 krb5_auth_con_free(callback_ctx
.context
, callback_ctx
.auth_context
);
331 krb5int_free_addrlist (&al
);
332 krb5_free_data_contents(callback_ctx
.context
, &callback_ctx
.ap_req
);
337 krb5_error_code KRB5_CALLCONV
338 krb5_change_password(krb5_context context
, krb5_creds
*creds
, char *newpw
, int *result_code
, krb5_data
*result_code_string
, krb5_data
*result_string
)
340 return krb5_change_set_password(
341 context
, creds
, newpw
, NULL
, result_code
, result_code_string
, result_string
);
345 * krb5_set_password - Implements set password per RFC 3244
349 krb5_error_code KRB5_CALLCONV
351 krb5_context context
,
354 krb5_principal change_password_for
,
355 int *result_code
, krb5_data
*result_code_string
, krb5_data
*result_string
358 return krb5_change_set_password(
359 context
, creds
, newpw
, change_password_for
, result_code
, result_code_string
, result_string
);
362 krb5_error_code KRB5_CALLCONV
363 krb5_set_password_using_ccache(
364 krb5_context context
,
367 krb5_principal change_password_for
,
368 int *result_code
, krb5_data
*result_code_string
, krb5_data
*result_string
373 krb5_error_code code
;
376 ** get the proper creds for use with krb5_set_password -
378 memset (&creds
, 0, sizeof(creds
));
380 ** first get the principal for the password service -
382 code
= krb5_cc_get_principal (context
, ccache
, &creds
.client
);
384 code
= krb5_build_principal(context
, &creds
.server
,
385 krb5_princ_realm(context
, change_password_for
)->length
,
386 krb5_princ_realm(context
, change_password_for
)->data
,
387 "kadmin", "changepw", NULL
);
389 code
= krb5_get_credentials(context
, 0, ccache
, &creds
, &credsp
);
391 code
= krb5_set_password(context
, credsp
, newpw
, change_password_for
,
392 result_code
, result_code_string
,
394 krb5_free_creds(context
, credsp
);
397 krb5_free_cred_contents(context
, &creds
);