xref: /freebsd/crypto/heimdal/appl/ftp/ftpd/gssapi.c (revision 9bd497b8354567454e075076d40c996e21bd6095)
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 */
529