1 /*
2 * lib/krb5/os/changepw.c
3 *
4 * Copyright 1990,1999,2001 by the Massachusetts Institute of Technology.
5 * All Rights Reserved.
6 *
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.
11 *
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.
25 *
26 */
27 /*
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
31 */
32
33 #include "fake-addrinfo.h"
34 #include "k5-int.h"
35 #include "os-proto.h"
36 #include "cm.h"
37
38 #include <stdio.h>
39 #include <errno.h>
40
41 #ifndef GETSOCKNAME_ARG3_TYPE
42 #define GETSOCKNAME_ARG3_TYPE int
43 #endif
44
45 struct sendto_callback_context {
46 krb5_context context;
47 krb5_auth_context auth_context;
48 krb5_principal set_password_for;
49 char *newpw;
50 krb5_data ap_req;
51 };
52
53
54 /*
55 * Wrapper function for the two backends
56 */
57
58 static krb5_error_code
krb5_locate_kpasswd(krb5_context context,const krb5_data * realm,struct addrlist * addrlist,krb5_boolean useTcp)59 krb5_locate_kpasswd(krb5_context context, const krb5_data *realm,
60 struct addrlist *addrlist, krb5_boolean useTcp)
61 {
62 krb5_error_code code;
63 int sockType = (useTcp ? SOCK_STREAM : SOCK_DGRAM);
64
65 code = krb5int_locate_server (context, realm, addrlist,
66 locate_service_kpasswd, sockType, 0);
67
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);
71 if (!code) {
72 /* Success with admin_server but now we need to change the
73 port number to use DEFAULT_KPASSWD_PORT and the socktype. */
74 int i;
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;
81 }
82 }
83 }
84 return (code);
85 }
86
87
88 /**
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
91 * password request.
92 */
93
94
kpasswd_sendto_msg_cleanup(void * callback_context,krb5_data * message)95 static void kpasswd_sendto_msg_cleanup (void* callback_context, krb5_data* message)
96 {
97 struct sendto_callback_context *ctx = callback_context;
98 krb5_free_data_contents(ctx->context, message);
99 }
100
101
kpasswd_sendto_msg_callback(struct conn_state * conn,void * callback_context,krb5_data * message)102 static int kpasswd_sendto_msg_callback(struct conn_state *conn, void *callback_context, krb5_data* message)
103 {
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;
109 krb5_data output;
110
111 memset (message, 0, sizeof(krb5_data));
112
113 /*
114 * We need the local addr from the connection socket
115 */
116 addrlen = sizeof(local_addr);
117
118 if (getsockname(conn->fd, ss2sa(&local_addr), &addrlen) < 0) {
119 code = SOCKET_ERRNO;
120 goto cleanup;
121 }
122
123 /* some brain-dead OS's don't return useful information from
124 * the getsockname call. Namely, windows and solaris. */
125
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;
130 } else {
131 krb5_address **addrs;
132
133 code = krb5_os_localaddr(ctx->context, &addrs);
134 if (code)
135 goto cleanup;
136
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) {
142 code = errno;
143 krb5_free_addresses(ctx->context, addrs);
144 goto cleanup;
145 }
146 memcpy(local_kaddr.contents, addrs[0]->contents, addrs[0]->length);
147
148 krb5_free_addresses(ctx->context, addrs);
149 }
150
151
152 /*
153 * TBD: Does this tamper w/ the auth context in such a way
154 * to break us? Yes - provide 1 per conn-state / host...
155 */
156
157
158 if ((code = krb5_auth_con_setaddrs(ctx->context, ctx->auth_context,
159 &local_kaddr, NULL)))
160 goto cleanup;
161
162 if (ctx->set_password_for)
163 code = krb5int_mk_setpw_req(ctx->context,
164 ctx->auth_context,
165 &ctx->ap_req,
166 ctx->set_password_for,
167 ctx->newpw,
168 &output);
169 else
170 code = krb5int_mk_chpw_req(ctx->context,
171 ctx->auth_context,
172 &ctx->ap_req,
173 ctx->newpw,
174 &output);
175 if (code)
176 goto cleanup;
177
178 message->length = output.length;
179 message->data = output.data;
180
181 cleanup:
182 return code;
183 }
184
185
186 /*
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
191 */
192 krb5_error_code KRB5_CALLCONV
krb5_change_set_password(krb5_context context,krb5_creds * creds,char * newpw,krb5_principal set_password_for,int * result_code,krb5_data * result_code_string,krb5_data * result_string)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)
197 {
198 krb5_data chpw_rep;
199 krb5_address remote_kaddr;
200 krb5_boolean useTcp = 0;
201 GETSOCKNAME_ARG3_TYPE addrlen;
202 krb5_error_code code = 0;
203 char *code_string;
204 int local_result_code;
205
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;
210
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;
215
216 if ((code = krb5_auth_con_init(callback_ctx.context,
217 &callback_ctx.auth_context)))
218 goto cleanup;
219
220 if ((code = krb5_mk_req_extended(callback_ctx.context,
221 &callback_ctx.auth_context,
222 AP_OPTS_USE_SUBKEY,
223 NULL,
224 creds,
225 &callback_ctx.ap_req)))
226 goto cleanup;
227
228 do {
229 if ((code = krb5_locate_kpasswd(callback_ctx.context,
230 krb5_princ_realm(callback_ctx.context,
231 creds->server),
232 &al, useTcp)))
233 break;
234
235 addrlen = sizeof(remote_addr);
236
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;
240
241 if ((code = krb5int_sendto(callback_ctx.context,
242 NULL,
243 &al,
244 &callback_info,
245 &chpw_rep,
246 NULL,
247 NULL,
248 ss2sa(&remote_addr),
249 &addrlen,
250 NULL,
251 NULL,
252 NULL
253 ))) {
254
255 /*
256 * Here we may want to switch to TCP on some errors.
257 * right?
258 */
259 break;
260 }
261
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;
265
266 if ((code = krb5_auth_con_setaddrs(callback_ctx.context,
267 callback_ctx.auth_context,
268 NULL,
269 &remote_kaddr)))
270 break;
271
272 if (set_password_for)
273 code = krb5int_rd_setpw_rep(callback_ctx.context,
274 callback_ctx.auth_context,
275 &chpw_rep,
276 &local_result_code,
277 result_string);
278 else
279 code = krb5int_rd_chpw_rep(callback_ctx.context,
280 callback_ctx.auth_context,
281 &chpw_rep,
282 &local_result_code,
283 result_string);
284
285 if (code) {
286 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) {
287 krb5int_free_addrlist (&al);
288 useTcp = 1;
289 continue;
290 }
291
292 break;
293 }
294
295 if (result_code)
296 *result_code = local_result_code;
297
298 if (result_code_string) {
299 if (set_password_for)
300 code = krb5int_setpw_result_code_string(callback_ctx.context,
301 local_result_code,
302 (const char **)&code_string);
303 else
304 code = krb5_chpw_result_code_string(callback_ctx.context,
305 local_result_code,
306 &code_string);
307 if(code)
308 goto cleanup;
309
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) {
313 code = ENOMEM;
314 goto cleanup;
315 }
316 strncpy(result_code_string->data, code_string, result_code_string->length);
317 }
318
319 if (code == KRB5KRB_ERR_RESPONSE_TOO_BIG && !useTcp ) {
320 krb5int_free_addrlist (&al);
321 useTcp = 1;
322 } else {
323 break;
324 }
325 } while (TRUE);
326
327 cleanup:
328 if (callback_ctx.auth_context != NULL)
329 krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context);
330
331 krb5int_free_addrlist (&al);
332 krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req);
333
334 return(code);
335 }
336
337 krb5_error_code KRB5_CALLCONV
krb5_change_password(krb5_context context,krb5_creds * creds,char * newpw,int * result_code,krb5_data * result_code_string,krb5_data * result_string)338 krb5_change_password(krb5_context context, krb5_creds *creds, char *newpw, int *result_code, krb5_data *result_code_string, krb5_data *result_string)
339 {
340 return krb5_change_set_password(
341 context, creds, newpw, NULL, result_code, result_code_string, result_string );
342 }
343
344 /*
345 * krb5_set_password - Implements set password per RFC 3244
346 *
347 */
348
349 krb5_error_code KRB5_CALLCONV
krb5_set_password(krb5_context context,krb5_creds * creds,char * newpw,krb5_principal change_password_for,int * result_code,krb5_data * result_code_string,krb5_data * result_string)350 krb5_set_password(
351 krb5_context context,
352 krb5_creds *creds,
353 char *newpw,
354 krb5_principal change_password_for,
355 int *result_code, krb5_data *result_code_string, krb5_data *result_string
356 )
357 {
358 return krb5_change_set_password(
359 context, creds, newpw, change_password_for, result_code, result_code_string, result_string );
360 }
361
362 krb5_error_code KRB5_CALLCONV
krb5_set_password_using_ccache(krb5_context context,krb5_ccache ccache,char * newpw,krb5_principal change_password_for,int * result_code,krb5_data * result_code_string,krb5_data * result_string)363 krb5_set_password_using_ccache(
364 krb5_context context,
365 krb5_ccache ccache,
366 char *newpw,
367 krb5_principal change_password_for,
368 int *result_code, krb5_data *result_code_string, krb5_data *result_string
369 )
370 {
371 krb5_creds creds;
372 krb5_creds *credsp;
373 krb5_error_code code;
374
375 /*
376 ** get the proper creds for use with krb5_set_password -
377 */
378 memset (&creds, 0, sizeof(creds));
379 /*
380 ** first get the principal for the password service -
381 */
382 code = krb5_cc_get_principal (context, ccache, &creds.client);
383 if (!code) {
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);
388 if (!code) {
389 code = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
390 if (!code) {
391 code = krb5_set_password(context, credsp, newpw, change_password_for,
392 result_code, result_code_string,
393 result_string);
394 krb5_free_creds(context, credsp);
395 }
396 }
397 krb5_free_cred_contents(context, &creds);
398 }
399 return code;
400 }
401