]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - crypto/heimdal/appl/ftp/ftp/gssapi.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / crypto / heimdal / appl / ftp / ftp / gssapi.c
1 /*
2  * Copyright (c) 1998 - 2005 Kungliga Tekniska Högskolan
3  * (Royal Institute of Technology, Stockholm, Sweden). 
4  * All rights reserved. 
5  *
6  * Redistribution and use in source and binary forms, with or without 
7  * modification, are permitted provided that the following conditions 
8  * are met: 
9  *
10  * 1. Redistributions of source code must retain the above copyright 
11  *    notice, this list of conditions and the following disclaimer. 
12  *
13  * 2. Redistributions in binary form must reproduce the above copyright 
14  *    notice, this list of conditions and the following disclaimer in the 
15  *    documentation and/or other materials provided with the distribution. 
16  *
17  * 3. Neither the name of the Institute nor the names of its contributors 
18  *    may be used to endorse or promote products derived from this software 
19  *    without specific prior written permission. 
20  *
21  * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 
22  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
23  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 
24  * ARE DISCLAIMED.  IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 
25  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 
26  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 
27  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
28  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
29  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 
30  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 
31  * SUCH DAMAGE. 
32  */
33
34 #ifdef FTP_SERVER
35 #include "ftpd_locl.h"
36 #else
37 #include "ftp_locl.h"
38 #endif
39 #include <gssapi.h>
40 #include <krb5_err.h>
41
42 RCSID("$Id: gssapi.c 21513 2007-07-12 12:45:25Z lha $");
43
44 int ftp_do_gss_bindings = 0;
45 int ftp_do_gss_delegate = 1;
46
47 struct gss_data {
48     gss_ctx_id_t context_hdl;
49     char *client_name;
50     gss_cred_id_t delegated_cred_handle;
51     void *mech_data;
52 };
53
54 static int
55 gss_init(void *app_data)
56 {
57     struct gss_data *d = app_data;
58     d->context_hdl = GSS_C_NO_CONTEXT;
59     d->delegated_cred_handle = GSS_C_NO_CREDENTIAL;
60 #if defined(FTP_SERVER)
61     return 0;
62 #else
63     /* XXX Check the gss mechanism; with  gss_indicate_mechs() ? */
64 #ifdef KRB5
65     return !use_kerberos;
66 #else
67     return 0;
68 #endif /* KRB5 */
69 #endif /* FTP_SERVER */
70 }
71
72 static int
73 gss_check_prot(void *app_data, int level)
74 {
75     if(level == prot_confidential)
76         return -1;
77     return 0;
78 }
79
80 static int
81 gss_decode(void *app_data, void *buf, int len, int level)
82 {
83     OM_uint32 maj_stat, min_stat;
84     gss_buffer_desc input, output;
85     gss_qop_t qop_state;
86     int conf_state;
87     struct gss_data *d = app_data;
88     size_t ret_len;
89
90     input.length = len;
91     input.value = buf;
92     maj_stat = gss_unwrap (&min_stat,
93                            d->context_hdl,
94                            &input,
95                            &output,
96                            &conf_state,
97                            &qop_state);
98     if(GSS_ERROR(maj_stat))
99         return -1;
100     memmove(buf, output.value, output.length);
101     ret_len = output.length;
102     gss_release_buffer(&min_stat, &output);
103     return ret_len;
104 }
105
106 static int
107 gss_overhead(void *app_data, int level, int len)
108 {
109     return 100; /* dunno? */
110 }
111
112
113 static int
114 gss_encode(void *app_data, void *from, int length, int level, void **to)
115 {
116     OM_uint32 maj_stat, min_stat;
117     gss_buffer_desc input, output;
118     int conf_state;
119     struct gss_data *d = app_data;
120
121     input.length = length;
122     input.value = from;
123     maj_stat = gss_wrap (&min_stat,
124                          d->context_hdl,
125                          level == prot_private,
126                          GSS_C_QOP_DEFAULT,
127                          &input,
128                          &conf_state,
129                          &output);
130     *to = output.value;
131     return output.length;
132 }
133
134 static void
135 sockaddr_to_gss_address (struct sockaddr *sa,
136                          OM_uint32 *addr_type,
137                          gss_buffer_desc *gss_addr)
138 {
139     switch (sa->sa_family) {
140 #ifdef HAVE_IPV6
141     case AF_INET6 : {
142         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
143
144         gss_addr->length = 16;
145         gss_addr->value  = &sin6->sin6_addr;
146         *addr_type       = GSS_C_AF_INET6;
147         break;
148     }
149 #endif
150     case AF_INET : {
151         struct sockaddr_in *sin4 = (struct sockaddr_in *)sa;
152
153         gss_addr->length = 4;
154         gss_addr->value  = &sin4->sin_addr;
155         *addr_type       = GSS_C_AF_INET;
156         break;
157     }
158     default :
159         errx (1, "unknown address family %d", sa->sa_family);
160         
161     }
162 }
163
164 /* end common stuff */
165
166 #ifdef FTP_SERVER
167
168 static int
169 gss_adat(void *app_data, void *buf, size_t len)
170 {
171     char *p = NULL;
172     gss_buffer_desc input_token, output_token;
173     OM_uint32 maj_stat, min_stat;
174     gss_name_t client_name;
175     struct gss_data *d = app_data;
176     gss_channel_bindings_t bindings;
177
178     if (ftp_do_gss_bindings) {
179         bindings = malloc(sizeof(*bindings));
180         if (bindings == NULL)
181             errx(1, "out of memory");
182
183         sockaddr_to_gss_address (his_addr,
184                                  &bindings->initiator_addrtype,
185                                  &bindings->initiator_address);
186         sockaddr_to_gss_address (ctrl_addr,
187                                  &bindings->acceptor_addrtype,
188                                  &bindings->acceptor_address);
189         
190         bindings->application_data.length = 0;
191         bindings->application_data.value = NULL;
192     } else
193         bindings = GSS_C_NO_CHANNEL_BINDINGS;
194
195     input_token.value = buf;
196     input_token.length = len;
197
198     maj_stat = gss_accept_sec_context (&min_stat,
199                                        &d->context_hdl,
200                                        GSS_C_NO_CREDENTIAL,
201                                        &input_token,
202                                        bindings,
203                                        &client_name,
204                                        NULL,
205                                        &output_token,
206                                        NULL,
207                                        NULL,
208                                        &d->delegated_cred_handle);
209
210     if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
211         free(bindings);
212
213     if(output_token.length) {
214         if(base64_encode(output_token.value, output_token.length, &p) < 0) {
215             reply(535, "Out of memory base64-encoding.");
216             return -1;
217         }
218         gss_release_buffer(&min_stat, &output_token);
219     }
220     if(maj_stat == GSS_S_COMPLETE){
221         char *name;
222         gss_buffer_desc export_name;
223         gss_OID oid;
224
225         maj_stat = gss_display_name(&min_stat, client_name,
226                                     &export_name, &oid);
227         if(maj_stat != 0) {
228             reply(500, "Error displaying name");
229             goto out;
230         }
231         /* XXX kerberos */
232         if(oid != GSS_KRB5_NT_PRINCIPAL_NAME) {
233             reply(500, "OID not kerberos principal name");
234             gss_release_buffer(&min_stat, &export_name);
235             goto out;
236         }
237         name = malloc(export_name.length + 1);
238         if(name == NULL) {
239             reply(500, "Out of memory");
240             gss_release_buffer(&min_stat, &export_name);
241             goto out;
242         }
243         memcpy(name, export_name.value, export_name.length);
244         name[export_name.length] = '\0';
245         gss_release_buffer(&min_stat, &export_name);
246         d->client_name = name;
247         if(p)
248             reply(235, "ADAT=%s", p);
249         else
250             reply(235, "ADAT Complete");
251         sec_complete = 1;
252
253     } else if(maj_stat == GSS_S_CONTINUE_NEEDED) {
254         if(p)
255             reply(335, "ADAT=%s", p);
256         else
257             reply(335, "OK, need more data");
258     } else {
259         OM_uint32 new_stat;
260         OM_uint32 msg_ctx = 0;
261         gss_buffer_desc status_string;
262         gss_display_status(&new_stat,
263                            min_stat,
264                            GSS_C_MECH_CODE,
265                            GSS_C_NO_OID,
266                            &msg_ctx,
267                            &status_string);
268         syslog(LOG_ERR, "gss_accept_sec_context: %s", 
269                (char*)status_string.value);
270         gss_release_buffer(&new_stat, &status_string);
271         reply(431, "Security resource unavailable");
272     }
273   out:
274     if (client_name)
275         gss_release_name(&min_stat, &client_name);
276     free(p);
277     return 0;
278 }
279
280 int gss_userok(void*, char*);
281 int gss_session(void*, char*);
282
283 struct sec_server_mech gss_server_mech = {
284     "GSSAPI",
285     sizeof(struct gss_data),
286     gss_init, /* init */
287     NULL, /* end */
288     gss_check_prot,
289     gss_overhead,
290     gss_encode,
291     gss_decode,
292     /* */
293     NULL,
294     gss_adat,
295     NULL, /* pbsz */
296     NULL, /* ccc */
297     gss_userok,
298     gss_session
299 };
300
301 #else /* FTP_SERVER */
302
303 extern struct sockaddr *hisctladdr, *myctladdr;
304
305 static int
306 import_name(const char *kname, const char *host, gss_name_t *target_name)
307 {
308     OM_uint32 maj_stat, min_stat;
309     gss_buffer_desc name;
310     char *str;
311
312     name.length = asprintf(&str, "%s@%s", kname, host);
313     if (str == NULL) {
314         printf("Out of memory\n");
315         return AUTH_ERROR;
316     }
317     name.value = str;
318
319     maj_stat = gss_import_name(&min_stat,
320                                &name,
321                                GSS_C_NT_HOSTBASED_SERVICE,
322                                target_name);
323     if (GSS_ERROR(maj_stat)) {
324         OM_uint32 new_stat;
325         OM_uint32 msg_ctx = 0;
326         gss_buffer_desc status_string;
327             
328         gss_display_status(&new_stat,
329                            min_stat,
330                            GSS_C_MECH_CODE,
331                            GSS_C_NO_OID,
332                            &msg_ctx,
333                            &status_string);
334         printf("Error importing name %s: %s\n", 
335                (char *)name.value,
336                (char *)status_string.value);
337         free(name.value);
338         gss_release_buffer(&new_stat, &status_string);
339         return AUTH_ERROR;
340     }
341     free(name.value);
342     return 0;
343 }
344
345 static int
346 gss_auth(void *app_data, char *host)
347 {
348     
349     OM_uint32 maj_stat, min_stat;
350     gss_name_t target_name;
351     gss_buffer_desc input, output_token;
352     int context_established = 0;
353     char *p;
354     int n;
355     gss_channel_bindings_t bindings;
356     struct gss_data *d = app_data;
357     OM_uint32 mech_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG;
358
359     const char *knames[] = { "ftp", "host", NULL }, **kname = knames;
360             
361     
362     if(import_name(*kname++, host, &target_name))
363         return AUTH_ERROR;
364
365     input.length = 0;
366     input.value = NULL;
367
368     if (ftp_do_gss_bindings) {
369         bindings = malloc(sizeof(*bindings));
370         if (bindings == NULL)
371             errx(1, "out of memory");
372         
373         sockaddr_to_gss_address (myctladdr,
374                                  &bindings->initiator_addrtype,
375                                  &bindings->initiator_address);
376         sockaddr_to_gss_address (hisctladdr,
377                                  &bindings->acceptor_addrtype,
378                                  &bindings->acceptor_address);
379         
380         bindings->application_data.length = 0;
381         bindings->application_data.value = NULL;
382     } else
383         bindings = GSS_C_NO_CHANNEL_BINDINGS;
384
385     if (ftp_do_gss_delegate)
386         mech_flags |= GSS_C_DELEG_FLAG;
387
388     while(!context_established) {
389         maj_stat = gss_init_sec_context(&min_stat,
390                                         GSS_C_NO_CREDENTIAL,
391                                         &d->context_hdl,
392                                         target_name,
393                                         GSS_C_NO_OID,
394                                         mech_flags,
395                                         0,
396                                         bindings,
397                                         &input,
398                                         NULL,
399                                         &output_token,
400                                         NULL,
401                                         NULL);
402         if (GSS_ERROR(maj_stat)) {
403             OM_uint32 new_stat;
404             OM_uint32 msg_ctx = 0;
405             gss_buffer_desc status_string;
406
407             d->context_hdl = GSS_C_NO_CONTEXT;
408
409             gss_release_name(&min_stat, &target_name);
410
411             if(*kname != NULL) {
412
413                 if(import_name(*kname++, host, &target_name)) {
414                     if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
415                         free(bindings);
416                     return AUTH_ERROR;
417                 }
418                 continue;
419             }
420             
421             if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
422                 free(bindings);
423
424             gss_display_status(&new_stat,
425                                min_stat,
426                                GSS_C_MECH_CODE,
427                                GSS_C_NO_OID,
428                                &msg_ctx,
429                                &status_string);
430             printf("Error initializing security context: %s\n", 
431                    (char*)status_string.value);
432             gss_release_buffer(&new_stat, &status_string);
433             return AUTH_CONTINUE;
434         }
435
436         if (input.value) {
437             free(input.value);
438             input.value = NULL;
439             input.length = 0;
440         }
441         if (output_token.length != 0) {
442             base64_encode(output_token.value, output_token.length, &p);
443             gss_release_buffer(&min_stat, &output_token);
444             n = command("ADAT %s", p);
445             free(p);
446         }
447         if (GSS_ERROR(maj_stat)) {
448             if (d->context_hdl != GSS_C_NO_CONTEXT)
449                 gss_delete_sec_context (&min_stat,
450                                         &d->context_hdl,
451                                         GSS_C_NO_BUFFER);
452             break;
453         }
454         if (maj_stat & GSS_S_CONTINUE_NEEDED) {
455             p = strstr(reply_string, "ADAT=");
456             if(p == NULL){
457                 printf("Error: expected ADAT in reply. got: %s\n",
458                        reply_string);
459                 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
460                     free(bindings);
461                 return AUTH_ERROR;
462             } else {
463                 p+=5;
464                 input.value = malloc(strlen(p));
465                 input.length = base64_decode(p, input.value);
466             }
467         } else {
468             if(code != 235) {
469                 printf("Unrecognized response code: %d\n", code);
470                 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
471                     free(bindings);
472                 return AUTH_ERROR;
473             }
474             context_established = 1;
475         }
476     }
477
478     gss_release_name(&min_stat, &target_name);
479
480     if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
481         free(bindings);
482     if (input.value)
483         free(input.value);
484
485     {
486         gss_name_t targ_name;
487
488         maj_stat = gss_inquire_context(&min_stat,
489                                        d->context_hdl,
490                                        NULL,
491                                        &targ_name,
492                                        NULL,
493                                        NULL,
494                                        NULL,
495                                        NULL,
496                                        NULL);
497         if (GSS_ERROR(maj_stat) == 0) {
498             gss_buffer_desc name;
499             maj_stat = gss_display_name (&min_stat,
500                                          targ_name,
501                                          &name,
502                                          NULL);
503             if (GSS_ERROR(maj_stat) == 0) {
504                 printf("Authenticated to <%s>\n", (char *)name.value);
505                 gss_release_buffer(&min_stat, &name);
506             }
507             gss_release_name(&min_stat, &targ_name);
508         } else
509             printf("Failed to get gss name of peer.\n");
510     }       
511
512
513     return AUTH_OK;
514 }
515
516 struct sec_client_mech gss_client_mech = {
517     "GSSAPI",
518     sizeof(struct gss_data),
519     gss_init,
520     gss_auth,
521     NULL, /* end */
522     gss_check_prot,
523     gss_overhead,
524     gss_encode,
525     gss_decode,
526 };
527
528 #endif /* FTP_SERVER */