remove gcc34
[dragonfly.git] / crypto / heimdal-0.6.3 / appl / ftp / ftp / gssapi.c
1 /*
2  * Copyright (c) 1998 - 2003 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,v 1.22.2.2 2003/08/20 16:41:24 lha Exp $");
43
44 int ftp_do_gss_bindings = 0;
45
46 struct gss_data {
47     gss_ctx_id_t context_hdl;
48     char *client_name;
49     gss_cred_id_t delegated_cred_handle;
50 };
51
52 static int
53 gss_init(void *app_data)
54 {
55     struct gss_data *d = app_data;
56     d->context_hdl = GSS_C_NO_CONTEXT;
57     d->delegated_cred_handle = NULL;
58 #if defined(FTP_SERVER)
59     return 0;
60 #else
61     /* XXX Check the gss mechanism; with  gss_indicate_mechs() ? */
62 #ifdef KRB5
63     return !use_kerberos;
64 #else
65     return 0
66 #endif /* KRB5 */
67 #endif /* FTP_SERVER */
68 }
69
70 static int
71 gss_check_prot(void *app_data, int level)
72 {
73     if(level == prot_confidential)
74         return -1;
75     return 0;
76 }
77
78 static int
79 gss_decode(void *app_data, void *buf, int len, int level)
80 {
81     OM_uint32 maj_stat, min_stat;
82     gss_buffer_desc input, output;
83     gss_qop_t qop_state;
84     int conf_state;
85     struct gss_data *d = app_data;
86     size_t ret_len;
87
88     input.length = len;
89     input.value = buf;
90     maj_stat = gss_unwrap (&min_stat,
91                            d->context_hdl,
92                            &input,
93                            &output,
94                            &conf_state,
95                            &qop_state);
96     if(GSS_ERROR(maj_stat))
97         return -1;
98     memmove(buf, output.value, output.length);
99     ret_len = output.length;
100     gss_release_buffer(&min_stat, &output);
101     return ret_len;
102 }
103
104 static int
105 gss_overhead(void *app_data, int level, int len)
106 {
107     return 100; /* dunno? */
108 }
109
110
111 static int
112 gss_encode(void *app_data, void *from, int length, int level, void **to)
113 {
114     OM_uint32 maj_stat, min_stat;
115     gss_buffer_desc input, output;
116     int conf_state;
117     struct gss_data *d = app_data;
118
119     input.length = length;
120     input.value = from;
121     maj_stat = gss_wrap (&min_stat,
122                          d->context_hdl,
123                          level == prot_private,
124                          GSS_C_QOP_DEFAULT,
125                          &input,
126                          &conf_state,
127                          &output);
128     *to = output.value;
129     return output.length;
130 }
131
132 static void
133 sockaddr_to_gss_address (const struct sockaddr *sa,
134                          OM_uint32 *addr_type,
135                          gss_buffer_desc *gss_addr)
136 {
137     switch (sa->sa_family) {
138 #ifdef HAVE_IPV6
139     case AF_INET6 : {
140         struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
141
142         gss_addr->length = 16;
143         gss_addr->value  = &sin6->sin6_addr;
144         *addr_type       = GSS_C_AF_INET6;
145         break;
146     }
147 #endif
148     case AF_INET : {
149         struct sockaddr_in *sin = (struct sockaddr_in *)sa;
150
151         gss_addr->length = 4;
152         gss_addr->value  = &sin->sin_addr;
153         *addr_type       = GSS_C_AF_INET;
154         break;
155     }
156     default :
157         errx (1, "unknown address family %d", sa->sa_family);
158         
159     }
160 }
161
162 /* end common stuff */
163
164 #ifdef FTP_SERVER
165
166 static int
167 gss_adat(void *app_data, void *buf, size_t len)
168 {
169     char *p = NULL;
170     gss_buffer_desc input_token, output_token;
171     OM_uint32 maj_stat, min_stat;
172     gss_name_t client_name;
173     struct gss_data *d = app_data;
174     gss_channel_bindings_t bindings;
175
176     if (ftp_do_gss_bindings) {
177         bindings = malloc(sizeof(*bindings));
178         if (bindings == NULL)
179             errx(1, "out of memory");
180
181         sockaddr_to_gss_address (his_addr,
182                                  &bindings->initiator_addrtype,
183                                  &bindings->initiator_address);
184         sockaddr_to_gss_address (ctrl_addr,
185                                  &bindings->acceptor_addrtype,
186                                  &bindings->acceptor_address);
187         
188         bindings->application_data.length = 0;
189         bindings->application_data.value = NULL;
190     } else
191         bindings = GSS_C_NO_CHANNEL_BINDINGS;
192
193     input_token.value = buf;
194     input_token.length = len;
195
196     d->delegated_cred_handle = malloc(sizeof(*d->delegated_cred_handle));
197     if (d->delegated_cred_handle == NULL) {
198         reply(500, "Out of memory");
199         goto out;
200     }
201
202     memset ((char*)d->delegated_cred_handle, 0,
203             sizeof(*d->delegated_cred_handle));
204     
205     maj_stat = gss_accept_sec_context (&min_stat,
206                                        &d->context_hdl,
207                                        GSS_C_NO_CREDENTIAL,
208                                        &input_token,
209                                        bindings,
210                                        &client_name,
211                                        NULL,
212                                        &output_token,
213                                        NULL,
214                                        NULL,
215                                        &d->delegated_cred_handle);
216
217     if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
218         free(bindings);
219
220     if(output_token.length) {
221         if(base64_encode(output_token.value, output_token.length, &p) < 0) {
222             reply(535, "Out of memory base64-encoding.");
223             return -1;
224         }
225     }
226     if(maj_stat == GSS_S_COMPLETE){
227         char *name;
228         gss_buffer_desc export_name;
229         gss_OID oid;
230
231         maj_stat = gss_display_name(&min_stat, client_name,
232                                     &export_name, &oid);
233         if(maj_stat != 0) {
234             reply(500, "Error displaying name");
235             goto out;
236         }
237         /* XXX kerberos */
238         if(oid != GSS_KRB5_NT_PRINCIPAL_NAME) {
239             reply(500, "OID not kerberos principal name");
240             gss_release_buffer(&min_stat, &export_name);
241             goto out;
242         }
243         name = malloc(export_name.length + 1);
244         if(name == NULL) {
245             reply(500, "Out of memory");
246             gss_release_buffer(&min_stat, &export_name);
247             goto out;
248         }
249         memcpy(name, export_name.value, export_name.length);
250         name[export_name.length] = '\0';
251         gss_release_buffer(&min_stat, &export_name);
252         d->client_name = name;
253         if(p)
254             reply(235, "ADAT=%s", p);
255         else
256             reply(235, "ADAT Complete");
257         sec_complete = 1;
258
259     } else if(maj_stat == GSS_S_CONTINUE_NEEDED) {
260         if(p)
261             reply(335, "ADAT=%s", p);
262         else
263             reply(335, "OK, need more data");
264     } else {
265         OM_uint32 new_stat;
266         OM_uint32 msg_ctx = 0;
267         gss_buffer_desc status_string;
268         gss_display_status(&new_stat,
269                            min_stat,
270                            GSS_C_MECH_CODE,
271                            GSS_C_NO_OID,
272                            &msg_ctx,
273                            &status_string);
274         syslog(LOG_ERR, "gss_accept_sec_context: %s", 
275                (char*)status_string.value);
276         gss_release_buffer(&new_stat, &status_string);
277         reply(431, "Security resource unavailable");
278     }
279   out:
280     free(p);
281     return 0;
282 }
283
284 int gss_userok(void*, char*);
285
286 struct sec_server_mech gss_server_mech = {
287     "GSSAPI",
288     sizeof(struct gss_data),
289     gss_init, /* init */
290     NULL, /* end */
291     gss_check_prot,
292     gss_overhead,
293     gss_encode,
294     gss_decode,
295     /* */
296     NULL,
297     gss_adat,
298     NULL, /* pbsz */
299     NULL, /* ccc */
300     gss_userok
301 };
302
303 #else /* FTP_SERVER */
304
305 extern struct sockaddr *hisctladdr, *myctladdr;
306
307 static int
308 import_name(const char *kname, const char *host, gss_name_t *target_name)
309 {
310     OM_uint32 maj_stat, min_stat;
311     gss_buffer_desc name;
312
313     name.length = asprintf((char**)&name.value, "%s@%s", kname, host);
314     if (name.value == NULL) {
315         printf("Out of memory\n");
316         return AUTH_ERROR;
317     }
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         gss_release_buffer(&new_stat, &status_string);
338         return AUTH_ERROR;
339     }
340     free(name.value);
341     return 0;
342 }
343
344 static int
345 gss_auth(void *app_data, char *host)
346 {
347     
348     OM_uint32 maj_stat, min_stat;
349     gss_name_t target_name;
350     gss_buffer_desc input, output_token;
351     int context_established = 0;
352     char *p;
353     int n;
354     gss_channel_bindings_t bindings;
355     struct gss_data *d = app_data;
356
357     const char *knames[] = { "ftp", "host", NULL }, **kname = knames;
358             
359     
360     if(import_name(*kname++, host, &target_name))
361         return AUTH_ERROR;
362
363     input.length = 0;
364     input.value = NULL;
365
366     if (ftp_do_gss_bindings) {
367         bindings = malloc(sizeof(*bindings));
368         if (bindings == NULL)
369             errx(1, "out of memory");
370         
371         sockaddr_to_gss_address (myctladdr,
372                                  &bindings->initiator_addrtype,
373                                  &bindings->initiator_address);
374         sockaddr_to_gss_address (hisctladdr,
375                                  &bindings->acceptor_addrtype,
376                                  &bindings->acceptor_address);
377         
378         bindings->application_data.length = 0;
379         bindings->application_data.value = NULL;
380     } else
381         bindings = GSS_C_NO_CHANNEL_BINDINGS;
382
383     while(!context_established) {
384         maj_stat = gss_init_sec_context(&min_stat,
385                                         GSS_C_NO_CREDENTIAL,
386                                         &d->context_hdl,
387                                         target_name,
388                                         GSS_C_NO_OID,
389                                         GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG
390                                           | GSS_C_DELEG_FLAG,
391                                         0,
392                                         bindings,
393                                         &input,
394                                         NULL,
395                                         &output_token,
396                                         NULL,
397                                         NULL);
398         if (GSS_ERROR(maj_stat)) {
399             OM_uint32 new_stat;
400             OM_uint32 msg_ctx = 0;
401             gss_buffer_desc status_string;
402
403             if(min_stat == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN && *kname != NULL) {
404                 if(import_name(*kname++, host, &target_name)) {
405                     if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
406                         free(bindings);
407                     return AUTH_ERROR;
408                 }
409                 continue;
410             }
411             
412             if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
413                 free(bindings);
414
415             gss_display_status(&new_stat,
416                                min_stat,
417                                GSS_C_MECH_CODE,
418                                GSS_C_NO_OID,
419                                &msg_ctx,
420                                &status_string);
421             printf("Error initializing security context: %s\n", 
422                    (char*)status_string.value);
423             gss_release_buffer(&new_stat, &status_string);
424             return AUTH_CONTINUE;
425         }
426
427         if (input.value) {
428             free(input.value);
429             input.value = NULL;
430             input.length = 0;
431         }
432         if (output_token.length != 0) {
433             base64_encode(output_token.value, output_token.length, &p);
434             gss_release_buffer(&min_stat, &output_token);
435             n = command("ADAT %s", p);
436             free(p);
437         }
438         if (GSS_ERROR(maj_stat)) {
439             if (d->context_hdl != GSS_C_NO_CONTEXT)
440                 gss_delete_sec_context (&min_stat,
441                                         &d->context_hdl,
442                                         GSS_C_NO_BUFFER);
443             break;
444         }
445         if (maj_stat & GSS_S_CONTINUE_NEEDED) {
446             p = strstr(reply_string, "ADAT=");
447             if(p == NULL){
448                 printf("Error: expected ADAT in reply. got: %s\n",
449                        reply_string);
450                 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
451                     free(bindings);
452                 return AUTH_ERROR;
453             } else {
454                 p+=5;
455                 input.value = malloc(strlen(p));
456                 input.length = base64_decode(p, input.value);
457             }
458         } else {
459             if(code != 235) {
460                 printf("Unrecognized response code: %d\n", code);
461                 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
462                     free(bindings);
463                 return AUTH_ERROR;
464             }
465             context_established = 1;
466         }
467     }
468
469     if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
470         free(bindings);
471     if (input.value)
472         free(input.value);
473
474     {
475         gss_name_t targ_name;
476
477         maj_stat = gss_inquire_context(&min_stat,
478                                        d->context_hdl,
479                                        NULL,
480                                        &targ_name,
481                                        NULL,
482                                        NULL,
483                                        NULL,
484                                        NULL,
485                                        NULL);
486         if (GSS_ERROR(maj_stat) == 0) {
487             gss_buffer_desc name;
488             maj_stat = gss_display_name (&min_stat,
489                                          targ_name,
490                                          &name,
491                                          NULL);
492             if (GSS_ERROR(maj_stat) == 0) {
493                 printf("Authenticated to <%s>\n", (char *)name.value);
494                 gss_release_buffer(&min_stat, &name);
495             }
496             gss_release_name(&min_stat, &targ_name);
497         } else
498             printf("Failed to get gss name of peer.\n");
499     }       
500
501
502     return AUTH_OK;
503 }
504
505 struct sec_client_mech gss_client_mech = {
506     "GSSAPI",
507     sizeof(struct gss_data),
508     gss_init,
509     gss_auth,
510     NULL, /* end */
511     gss_check_prot,
512     gss_overhead,
513     gss_encode,
514     gss_decode,
515 };
516
517 #endif /* FTP_SERVER */