2 * Copyright (c) 1998 - 2003 Kungliga Tekniska Högskolan
3 * (Royal Institute of Technology, Stockholm, Sweden).
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
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
35 #include "ftpd_locl.h"
42 RCSID("$Id: gssapi.c,v 1.22.2.2 2003/08/20 16:41:24 lha Exp $");
44 int ftp_do_gss_bindings = 0;
47 gss_ctx_id_t context_hdl;
49 gss_cred_id_t delegated_cred_handle;
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)
61 /* XXX Check the gss mechanism; with gss_indicate_mechs() ? */
67 #endif /* FTP_SERVER */
71 gss_check_prot(void *app_data, int level)
73 if(level == prot_confidential)
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;
85 struct gss_data *d = app_data;
90 maj_stat = gss_unwrap (&min_stat,
96 if(GSS_ERROR(maj_stat))
98 memmove(buf, output.value, output.length);
99 ret_len = output.length;
100 gss_release_buffer(&min_stat, &output);
105 gss_overhead(void *app_data, int level, int len)
107 return 100; /* dunno? */
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;
117 struct gss_data *d = app_data;
119 input.length = length;
121 maj_stat = gss_wrap (&min_stat,
123 level == prot_private,
129 return output.length;
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) {
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;
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;
157 errx (1, "unknown address family %d", sa->sa_family);
162 /* end common stuff */
167 gss_adat(void *app_data, void *buf, size_t len)
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;
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");
202 memset ((char*)d->delegated_cred_handle, 0,
203 sizeof(*d->delegated_cred_handle));
205 maj_stat = gss_accept_sec_context (&min_stat,
215 &d->delegated_cred_handle);
217 if (bindings != GSS_C_NO_CHANNEL_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.");
226 if(maj_stat == GSS_S_COMPLETE){
228 gss_buffer_desc export_name;
231 maj_stat = gss_display_name(&min_stat, client_name,
234 reply(500, "Error displaying name");
238 if(oid != GSS_KRB5_NT_PRINCIPAL_NAME) {
239 reply(500, "OID not kerberos principal name");
240 gss_release_buffer(&min_stat, &export_name);
243 name = malloc(export_name.length + 1);
245 reply(500, "Out of memory");
246 gss_release_buffer(&min_stat, &export_name);
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;
254 reply(235, "ADAT=%s", p);
256 reply(235, "ADAT Complete");
259 } else if(maj_stat == GSS_S_CONTINUE_NEEDED) {
261 reply(335, "ADAT=%s", p);
263 reply(335, "OK, need more data");
266 OM_uint32 msg_ctx = 0;
267 gss_buffer_desc status_string;
268 gss_display_status(&new_stat,
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");
284 int gss_userok(void*, char*);
286 struct sec_server_mech gss_server_mech = {
288 sizeof(struct gss_data),
303 #else /* FTP_SERVER */
305 extern struct sockaddr *hisctladdr, *myctladdr;
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");
319 maj_stat = gss_import_name(&min_stat,
321 GSS_C_NT_HOSTBASED_SERVICE,
323 if (GSS_ERROR(maj_stat)) {
325 OM_uint32 msg_ctx = 0;
326 gss_buffer_desc status_string;
328 gss_display_status(&new_stat,
334 printf("Error importing name %s: %s\n",
336 (char *)status_string.value);
337 gss_release_buffer(&new_stat, &status_string);
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;
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))
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;
381 bindings = GSS_C_NO_CHANNEL_BINDINGS;
383 while(!context_established) {
384 maj_stat = gss_init_sec_context(&min_stat,
389 GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG
398 if (GSS_ERROR(maj_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)
412 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
415 gss_display_status(&new_stat,
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;
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);
438 if (GSS_ERROR(maj_stat)) {
439 if (d->context_hdl != GSS_C_NO_CONTEXT)
440 gss_delete_sec_context (&min_stat,
445 if (maj_stat & GSS_S_CONTINUE_NEEDED) {
446 p = strstr(reply_string, "ADAT=");
448 printf("Error: expected ADAT in reply. got: %s\n",
450 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
455 input.value = malloc(strlen(p));
456 input.length = base64_decode(p, input.value);
460 printf("Unrecognized response code: %d\n", code);
461 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
465 context_established = 1;
469 if (bindings != GSS_C_NO_CHANNEL_BINDINGS)
475 gss_name_t targ_name;
477 maj_stat = gss_inquire_context(&min_stat,
486 if (GSS_ERROR(maj_stat) == 0) {
487 gss_buffer_desc name;
488 maj_stat = gss_display_name (&min_stat,
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);
498 printf("Failed to get gss name of peer.\n");
505 struct sec_client_mech gss_client_mech = {
507 sizeof(struct gss_data),
517 #endif /* FTP_SERVER */