xref: /freebsd/crypto/krb5/src/lib/krb5/os/changepw.c (revision 7f2fe78b9dd5f51c821d771b63d2e096f6fd49e9)
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