This commit was manufactured by cvs2svn to create tag
[heimdal.git] / appl / ftp / ftp / gssapi.c
blobb5ab06970cf742a9835141830059374e2b27af02
1 /*
2 * Copyright (c) 1998 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
4 * All rights reserved.
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
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.
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.
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.
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>
42 RCSID("$Id$");
44 int ftp_do_gss_bindings = 0;
46 struct gss_data {
47 gss_ctx_id_t context_hdl;
48 char *client_name;
49 gss_cred_id_t delegated_cred_handle;
52 static int
53 gss_init(void *app_data)
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 */
70 static int
71 gss_check_prot(void *app_data, int level)
73 if(level == prot_confidential)
74 return -1;
75 return 0;
78 static int
79 gss_decode(void *app_data, void *buf, int len, int level)
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;
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;
104 static int
105 gss_overhead(void *app_data, int level, int len)
107 return 100; /* dunno? */
111 static int
112 gss_encode(void *app_data, void *from, int length, int level, void **to)
114 OM_uint32 maj_stat, min_stat;
115 gss_buffer_desc input, output;
116 int conf_state;
117 struct gss_data *d = app_data;
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;
132 static void
133 sockaddr_to_gss_address (const struct sockaddr *sa,
134 OM_uint32 *addr_type,
135 gss_buffer_desc *gss_addr)
137 switch (sa->sa_family) {
138 #ifdef HAVE_IPV6
139 case AF_INET6 : {
140 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa;
142 gss_addr->length = 16;
143 gss_addr->value = &sin6->sin6_addr;
144 *addr_type = GSS_C_AF_INET6;
145 break;
147 #endif
148 case AF_INET : {
149 struct sockaddr_in *sin = (struct sockaddr_in *)sa;
151 gss_addr->length = 4;
152 gss_addr->value = &sin->sin_addr;
153 *addr_type = GSS_C_AF_INET;
154 break;
156 default :
157 errx (1, "unknown address family %d", sa->sa_family);
162 /* end common stuff */
164 #ifdef FTP_SERVER
166 static int
167 gss_adat(void *app_data, void *buf, size_t len)
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;
176 if (ftp_do_gss_bindings) {
177 bindings = malloc(sizeof(*bindings));
178 if (bindings == NULL)
179 errx(1, "out of memory");
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);
188 bindings->application_data.length = 0;
189 bindings->application_data.value = NULL;
190 } else
191 bindings = GSS_C_NO_CHANNEL_BINDINGS;
193 input_token.value = buf;
194 input_token.length = len;
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;
202 memset ((char*)d->delegated_cred_handle, 0,
203 sizeof(*d->delegated_cred_handle));
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);
217 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
218 free(bindings);
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;
226 if(maj_stat == GSS_S_COMPLETE){
227 char *name;
228 gss_buffer_desc export_name;
229 gss_OID oid;
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;
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;
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;
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;
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");
279 out:
280 free(p);
281 return 0;
284 int gss_userok(void*, char*);
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
303 #else /* FTP_SERVER */
305 extern struct sockaddr *hisctladdr, *myctladdr;
307 static int
308 import_name(const char *kname, const char *host, gss_name_t *target_name)
310 OM_uint32 maj_stat, min_stat;
311 gss_buffer_desc name;
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;
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;
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;
340 free(name.value);
341 return 0;
344 static int
345 gss_auth(void *app_data, char *host)
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;
357 const char *knames[] = { "ftp", "host", NULL }, **kname = knames;
360 if(import_name(*kname++, host, &target_name))
361 return AUTH_ERROR;
363 input.length = 0;
364 input.value = NULL;
366 if (ftp_do_gss_bindings) {
367 bindings = malloc(sizeof(*bindings));
368 if (bindings == NULL)
369 errx(1, "out of memory");
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);
378 bindings->application_data.length = 0;
379 bindings->application_data.value = NULL;
380 } else
381 bindings = GSS_C_NO_CHANNEL_BINDINGS;
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,
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;
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;
409 continue;
412 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
413 free(bindings);
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;
427 if (input.value) {
428 free(input.value);
429 input.value = NULL;
430 input.length = 0;
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);
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;
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);
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;
465 context_established = 1;
469 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
470 free(bindings);
471 if (input.value)
472 free(input.value);
475 gss_name_t targ_name;
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);
496 gss_release_name(&min_stat, &targ_name);
497 } else
498 printf("Failed to get gss name of peer.\n");
502 return AUTH_OK;
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,
517 #endif /* FTP_SERVER */