1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/os/changepw.c */
3 /*
4 * Copyright 1990,1999,2001,2008 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
31 * infrastructure
32 */
33
34 #include "k5-int.h"
35 #include "fake-addrinfo.h"
36 #include "os-proto.h"
37 #include "../krb/auth_con.h"
38 #include "../krb/int-proto.h"
39
40 #include <stdio.h>
41 #include <errno.h>
42
43 #ifndef GETSOCKNAME_ARG3_TYPE
44 #define GETSOCKNAME_ARG3_TYPE int
45 #endif
46
47 struct sendto_callback_context {
48 krb5_context context;
49 krb5_auth_context auth_context;
50 krb5_principal set_password_for;
51 const char *newpw;
52 krb5_data ap_req;
53 krb5_ui_4 remote_seq_num, local_seq_num;
54 };
55
56 /*
57 * Wrapper function for the two backends
58 */
59
60 static krb5_error_code
locate_kpasswd(krb5_context context,const krb5_data * realm,struct serverlist * serverlist)61 locate_kpasswd(krb5_context context, const krb5_data *realm,
62 struct serverlist *serverlist)
63 {
64 krb5_error_code code;
65
66 code = k5_locate_server(context, realm, serverlist, locate_service_kpasswd,
67 FALSE);
68 if (code == KRB5_REALM_CANT_RESOLVE || code == KRB5_REALM_UNKNOWN) {
69 code = k5_locate_server(context, realm, serverlist,
70 locate_service_kadmin, TRUE);
71 if (!code) {
72 /* Success with admin_server but now we need to change the port
73 * number to use DEFAULT_KPASSWD_PORT and the transport. */
74 size_t i;
75 for (i = 0; i < serverlist->nservers; i++) {
76 struct server_entry *s = &serverlist->servers[i];
77
78 if (s->transport == TCP)
79 s->transport = TCP_OR_UDP;
80 if (s->hostname != NULL)
81 s->port = DEFAULT_KPASSWD_PORT;
82 else if (s->family == AF_INET)
83 ss2sin(&s->addr)->sin_port = htons(DEFAULT_KPASSWD_PORT);
84 else if (s->family == AF_INET6)
85 ss2sin6(&s->addr)->sin6_port = htons(DEFAULT_KPASSWD_PORT);
86 }
87 }
88 }
89 return (code);
90 }
91
92
93 /**
94 * This routine is used for a callback in sendto_kdc.c code. Simply
95 * put, we need the client addr to build the krb_priv portion of the
96 * password request.
97 */
98
99
100 static void
kpasswd_sendto_msg_cleanup(void * data,krb5_data * message)101 kpasswd_sendto_msg_cleanup(void *data, krb5_data *message)
102 {
103 struct sendto_callback_context *ctx = data;
104
105 krb5_free_data_contents(ctx->context, message);
106 }
107
108
109 static int
kpasswd_sendto_msg_callback(SOCKET fd,void * data,krb5_data * message)110 kpasswd_sendto_msg_callback(SOCKET fd, void *data, krb5_data *message)
111 {
112 krb5_error_code code = 0;
113 struct sockaddr_storage local_addr;
114 krb5_address local_kaddr;
115 struct sendto_callback_context *ctx = data;
116 GETSOCKNAME_ARG3_TYPE addrlen;
117 krb5_data output;
118
119 memset (message, 0, sizeof(krb5_data));
120
121 /*
122 * We need the local addr from the connection socket
123 */
124 addrlen = sizeof(local_addr);
125
126 if (getsockname(fd, ss2sa(&local_addr), &addrlen) < 0) {
127 code = SOCKET_ERRNO;
128 goto cleanup;
129 }
130
131 /* some brain-dead OS's don't return useful information from
132 * the getsockname call. Namely, windows and solaris. */
133
134 if (local_addr.ss_family == AF_INET &&
135 ss2sin(&local_addr)->sin_addr.s_addr != 0) {
136 local_kaddr.addrtype = ADDRTYPE_INET;
137 local_kaddr.length = sizeof(ss2sin(&local_addr)->sin_addr);
138 local_kaddr.contents = (krb5_octet *) &ss2sin(&local_addr)->sin_addr;
139 } else if (local_addr.ss_family == AF_INET6 &&
140 memcmp(ss2sin6(&local_addr)->sin6_addr.s6_addr,
141 in6addr_any.s6_addr, sizeof(in6addr_any.s6_addr)) != 0) {
142 local_kaddr.addrtype = ADDRTYPE_INET6;
143 local_kaddr.length = sizeof(ss2sin6(&local_addr)->sin6_addr);
144 local_kaddr.contents = (krb5_octet *) &ss2sin6(&local_addr)->sin6_addr;
145 } else {
146 krb5_address **addrs;
147
148 code = krb5_os_localaddr(ctx->context, &addrs);
149 if (code)
150 goto cleanup;
151
152 local_kaddr.magic = addrs[0]->magic;
153 local_kaddr.addrtype = addrs[0]->addrtype;
154 local_kaddr.length = addrs[0]->length;
155 local_kaddr.contents = k5memdup(addrs[0]->contents, addrs[0]->length,
156 &code);
157 krb5_free_addresses(ctx->context, addrs);
158 if (local_kaddr.contents == NULL)
159 goto cleanup;
160 }
161
162
163 /*
164 * TBD: Does this tamper w/ the auth context in such a way
165 * to break us? Yes - provide 1 per conn-state / host...
166 */
167
168
169 if ((code = krb5_auth_con_setaddrs(ctx->context, ctx->auth_context,
170 &local_kaddr, NULL)))
171 goto cleanup;
172
173 ctx->auth_context->remote_seq_number = ctx->remote_seq_num;
174 ctx->auth_context->local_seq_number = ctx->local_seq_num;
175
176 if (ctx->set_password_for)
177 code = krb5int_mk_setpw_req(ctx->context,
178 ctx->auth_context,
179 &ctx->ap_req,
180 ctx->set_password_for,
181 ctx->newpw,
182 &output);
183 else
184 code = krb5int_mk_chpw_req(ctx->context,
185 ctx->auth_context,
186 &ctx->ap_req,
187 ctx->newpw,
188 &output);
189 if (code)
190 goto cleanup;
191
192 message->length = output.length;
193 message->data = output.data;
194
195 cleanup:
196 return code;
197 }
198
199
200 /*
201 ** The logic for setting and changing a password is mostly the same
202 ** change_set_password handles both cases
203 ** if set_password_for is NULL, then a password change is performed,
204 ** otherwise, the password is set for the principal indicated in set_password_for
205 */
206 static krb5_error_code
change_set_password(krb5_context context,krb5_creds * creds,const char * newpw,krb5_principal set_password_for,int * result_code,krb5_data * result_code_string,krb5_data * result_string)207 change_set_password(krb5_context context,
208 krb5_creds *creds,
209 const char *newpw,
210 krb5_principal set_password_for,
211 int *result_code,
212 krb5_data *result_code_string,
213 krb5_data *result_string)
214 {
215 krb5_data chpw_rep;
216 GETSOCKNAME_ARG3_TYPE addrlen;
217 krb5_error_code code = 0;
218 char *code_string;
219 int local_result_code;
220
221 struct sendto_callback_context callback_ctx;
222 struct sendto_callback_info callback_info;
223 struct sockaddr_storage remote_addr;
224 struct serverlist sl = SERVERLIST_INIT;
225
226 memset(&chpw_rep, 0, sizeof(krb5_data));
227 memset( &callback_ctx, 0, sizeof(struct sendto_callback_context));
228 callback_ctx.context = context;
229 callback_ctx.newpw = newpw;
230 callback_ctx.set_password_for = set_password_for;
231
232 if ((code = krb5_auth_con_init(callback_ctx.context,
233 &callback_ctx.auth_context)))
234 goto cleanup;
235
236 if ((code = krb5_mk_req_extended(callback_ctx.context,
237 &callback_ctx.auth_context,
238 AP_OPTS_USE_SUBKEY,
239 NULL,
240 creds,
241 &callback_ctx.ap_req)))
242 goto cleanup;
243
244 callback_ctx.remote_seq_num = callback_ctx.auth_context->remote_seq_number;
245 callback_ctx.local_seq_num = callback_ctx.auth_context->local_seq_number;
246
247 code = locate_kpasswd(callback_ctx.context, &creds->server->realm, &sl);
248 if (code)
249 goto cleanup;
250
251 addrlen = sizeof(remote_addr);
252
253 callback_info.data = &callback_ctx;
254 callback_info.pfn_callback = kpasswd_sendto_msg_callback;
255 callback_info.pfn_cleanup = kpasswd_sendto_msg_cleanup;
256 krb5_free_data_contents(callback_ctx.context, &chpw_rep);
257
258 /* UDP retransmits may be seen as replays. Only try UDP after other
259 * transports fail completely. */
260 code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
261 &sl, NO_UDP, &callback_info, &chpw_rep,
262 ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
263 if (code == KRB5_KDC_UNREACH) {
264 code = k5_sendto(callback_ctx.context, NULL, &creds->server->realm,
265 &sl, ONLY_UDP, &callback_info, &chpw_rep,
266 ss2sa(&remote_addr), &addrlen, NULL, NULL, NULL);
267 }
268 if (code)
269 goto cleanup;
270
271 code = krb5int_rd_chpw_rep(callback_ctx.context,
272 callback_ctx.auth_context,
273 &chpw_rep, &local_result_code,
274 result_string);
275
276 if (code)
277 goto cleanup;
278
279 if (result_code)
280 *result_code = local_result_code;
281
282 if (result_code_string) {
283 code = krb5_chpw_result_code_string(callback_ctx.context,
284 local_result_code,
285 &code_string);
286 if (code)
287 goto cleanup;
288
289 result_code_string->length = strlen(code_string);
290 result_code_string->data = malloc(result_code_string->length);
291 if (result_code_string->data == NULL) {
292 code = ENOMEM;
293 goto cleanup;
294 }
295 strncpy(result_code_string->data, code_string, result_code_string->length);
296 }
297
298 cleanup:
299 if (callback_ctx.auth_context != NULL)
300 krb5_auth_con_free(callback_ctx.context, callback_ctx.auth_context);
301
302 k5_free_serverlist(&sl);
303 krb5_free_data_contents(callback_ctx.context, &callback_ctx.ap_req);
304 krb5_free_data_contents(callback_ctx.context, &chpw_rep);
305
306 return(code);
307 }
308
309 krb5_error_code KRB5_CALLCONV
krb5_change_password(krb5_context context,krb5_creds * creds,const char * newpw,int * result_code,krb5_data * result_code_string,krb5_data * result_string)310 krb5_change_password(krb5_context context,
311 krb5_creds *creds,
312 const char *newpw,
313 int *result_code,
314 krb5_data *result_code_string,
315 krb5_data *result_string)
316 {
317 return change_set_password(context, creds, newpw, NULL,
318 result_code, result_code_string, result_string );
319 }
320
321 /*
322 * krb5_set_password - Implements set password per RFC 3244
323 *
324 */
325
326 krb5_error_code KRB5_CALLCONV
krb5_set_password(krb5_context context,krb5_creds * creds,const char * newpw,krb5_principal change_password_for,int * result_code,krb5_data * result_code_string,krb5_data * result_string)327 krb5_set_password(krb5_context context,
328 krb5_creds *creds,
329 const char *newpw,
330 krb5_principal change_password_for,
331 int *result_code,
332 krb5_data *result_code_string,
333 krb5_data *result_string
334 )
335 {
336 return change_set_password(context, creds, newpw, change_password_for,
337 result_code, result_code_string, result_string );
338 }
339
340 krb5_error_code KRB5_CALLCONV
krb5_set_password_using_ccache(krb5_context context,krb5_ccache ccache,const char * newpw,krb5_principal change_password_for,int * result_code,krb5_data * result_code_string,krb5_data * result_string)341 krb5_set_password_using_ccache(krb5_context context,
342 krb5_ccache ccache,
343 const char *newpw,
344 krb5_principal change_password_for,
345 int *result_code,
346 krb5_data *result_code_string,
347 krb5_data *result_string
348 )
349 {
350 krb5_creds creds;
351 krb5_creds *credsp;
352 krb5_error_code code;
353
354 /*
355 ** get the proper creds for use with krb5_set_password -
356 */
357 memset (&creds, 0, sizeof(creds));
358 /*
359 ** first get the principal for the password service -
360 */
361 code = krb5_cc_get_principal (context, ccache, &creds.client);
362 if (!code) {
363 code = krb5_build_principal(context, &creds.server,
364 change_password_for->realm.length,
365 change_password_for->realm.data,
366 "kadmin", "changepw", NULL);
367 if (!code) {
368 code = krb5_get_credentials(context, 0, ccache, &creds, &credsp);
369 if (!code) {
370 code = krb5_set_password(context, credsp, newpw, change_password_for,
371 result_code, result_code_string,
372 result_string);
373 krb5_free_creds(context, credsp);
374 }
375 }
376 krb5_free_cred_contents(context, &creds);
377 }
378 return code;
379 }
380