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"
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 */