xref: /freebsd/crypto/heimdal/appl/ftp/ftp/gssapi.c (revision 7bd6fde3)
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 */
518