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