2 * Copyright (c) 1998 - 2005 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("$Heimdal: gssapi.c 21513 2007-07-12 12:45:25Z lha $"
45 int ftp_do_gss_bindings
= 0;
46 int ftp_do_gss_delegate
= 1;
49 gss_ctx_id_t context_hdl
;
51 gss_cred_id_t delegated_cred_handle
;
56 gss_init(void *app_data
)
58 struct gss_data
*d
= app_data
;
59 d
->context_hdl
= GSS_C_NO_CONTEXT
;
60 d
->delegated_cred_handle
= GSS_C_NO_CREDENTIAL
;
61 #if defined(FTP_SERVER)
64 /* XXX Check the gss mechanism; with gss_indicate_mechs() ? */
70 #endif /* FTP_SERVER */
74 gss_check_prot(void *app_data
, int level
)
76 if(level
== prot_confidential
)
82 gss_decode(void *app_data
, void *buf
, int len
, int level
)
84 OM_uint32 maj_stat
, min_stat
;
85 gss_buffer_desc input
, output
;
88 struct gss_data
*d
= app_data
;
93 maj_stat
= gss_unwrap (&min_stat
,
99 if(GSS_ERROR(maj_stat
))
101 memmove(buf
, output
.value
, output
.length
);
102 ret_len
= output
.length
;
103 gss_release_buffer(&min_stat
, &output
);
108 gss_overhead(void *app_data
, int level
, int len
)
110 return 100; /* dunno? */
115 gss_encode(void *app_data
, void *from
, int length
, int level
, void **to
)
117 OM_uint32 maj_stat
, min_stat
;
118 gss_buffer_desc input
, output
;
120 struct gss_data
*d
= app_data
;
122 input
.length
= length
;
124 maj_stat
= gss_wrap (&min_stat
,
126 level
== prot_private
,
132 return output
.length
;
136 sockaddr_to_gss_address (struct sockaddr
*sa
,
137 OM_uint32
*addr_type
,
138 gss_buffer_desc
*gss_addr
)
140 switch (sa
->sa_family
) {
143 struct sockaddr_in6
*sin6
= (struct sockaddr_in6
*)sa
;
145 gss_addr
->length
= 16;
146 gss_addr
->value
= &sin6
->sin6_addr
;
147 *addr_type
= GSS_C_AF_INET6
;
152 struct sockaddr_in
*sin4
= (struct sockaddr_in
*)sa
;
154 gss_addr
->length
= 4;
155 gss_addr
->value
= &sin4
->sin_addr
;
156 *addr_type
= GSS_C_AF_INET
;
160 errx (1, "unknown address family %d", sa
->sa_family
);
165 /* end common stuff */
170 gss_adat(void *app_data
, void *buf
, size_t len
)
173 gss_buffer_desc input_token
, output_token
;
174 OM_uint32 maj_stat
, min_stat
;
175 gss_name_t client_name
;
176 struct gss_data
*d
= app_data
;
177 gss_channel_bindings_t bindings
;
179 if (ftp_do_gss_bindings
) {
180 bindings
= malloc(sizeof(*bindings
));
181 if (bindings
== NULL
)
182 errx(1, "out of memory");
184 sockaddr_to_gss_address (his_addr
,
185 &bindings
->initiator_addrtype
,
186 &bindings
->initiator_address
);
187 sockaddr_to_gss_address (ctrl_addr
,
188 &bindings
->acceptor_addrtype
,
189 &bindings
->acceptor_address
);
191 bindings
->application_data
.length
= 0;
192 bindings
->application_data
.value
= NULL
;
194 bindings
= GSS_C_NO_CHANNEL_BINDINGS
;
196 input_token
.value
= buf
;
197 input_token
.length
= len
;
199 maj_stat
= gss_accept_sec_context (&min_stat
,
209 &d
->delegated_cred_handle
);
211 if (bindings
!= GSS_C_NO_CHANNEL_BINDINGS
)
214 if(output_token
.length
) {
215 if(base64_encode(output_token
.value
, output_token
.length
, &p
) < 0) {
216 reply(535, "Out of memory base64-encoding.");
219 gss_release_buffer(&min_stat
, &output_token
);
221 if(maj_stat
== GSS_S_COMPLETE
){
223 gss_buffer_desc export_name
;
226 maj_stat
= gss_display_name(&min_stat
, client_name
,
229 reply(500, "Error displaying name");
233 if(oid
!= GSS_KRB5_NT_PRINCIPAL_NAME
) {
234 reply(500, "OID not kerberos principal name");
235 gss_release_buffer(&min_stat
, &export_name
);
238 name
= malloc(export_name
.length
+ 1);
240 reply(500, "Out of memory");
241 gss_release_buffer(&min_stat
, &export_name
);
244 memcpy(name
, export_name
.value
, export_name
.length
);
245 name
[export_name
.length
] = '\0';
246 gss_release_buffer(&min_stat
, &export_name
);
247 d
->client_name
= name
;
249 reply(235, "ADAT=%s", p
);
251 reply(235, "ADAT Complete");
254 } else if(maj_stat
== GSS_S_CONTINUE_NEEDED
) {
256 reply(335, "ADAT=%s", p
);
258 reply(335, "OK, need more data");
261 OM_uint32 msg_ctx
= 0;
262 gss_buffer_desc status_string
;
263 gss_display_status(&new_stat
,
269 syslog(LOG_ERR
, "gss_accept_sec_context: %s",
270 (char*)status_string
.value
);
271 gss_release_buffer(&new_stat
, &status_string
);
272 reply(431, "Security resource unavailable");
276 gss_release_name(&min_stat
, &client_name
);
281 int gss_userok(void*, char*);
282 int gss_session(void*, char*);
284 struct sec_server_mech gss_server_mech
= {
286 sizeof(struct gss_data
),
302 #else /* FTP_SERVER */
304 extern struct sockaddr
*hisctladdr
, *myctladdr
;
307 import_name(const char *kname
, const char *host
, gss_name_t
*target_name
)
309 OM_uint32 maj_stat
, min_stat
;
310 gss_buffer_desc name
;
313 name
.length
= asprintf(&str
, "%s@%s", kname
, host
);
315 printf("Out of memory\n");
320 maj_stat
= gss_import_name(&min_stat
,
322 GSS_C_NT_HOSTBASED_SERVICE
,
324 if (GSS_ERROR(maj_stat
)) {
326 OM_uint32 msg_ctx
= 0;
327 gss_buffer_desc status_string
;
329 gss_display_status(&new_stat
,
335 printf("Error importing name %s: %s\n",
337 (char *)status_string
.value
);
339 gss_release_buffer(&new_stat
, &status_string
);
347 gss_auth(void *app_data
, char *host
)
350 OM_uint32 maj_stat
, min_stat
;
351 gss_name_t target_name
;
352 gss_buffer_desc input
, output_token
;
353 int context_established
= 0;
356 gss_channel_bindings_t bindings
;
357 struct gss_data
*d
= app_data
;
358 OM_uint32 mech_flags
= GSS_C_MUTUAL_FLAG
| GSS_C_SEQUENCE_FLAG
;
360 const char *knames
[] = { "ftp", "host", NULL
}, **kname
= knames
;
363 if(import_name(*kname
++, host
, &target_name
))
369 if (ftp_do_gss_bindings
) {
370 bindings
= malloc(sizeof(*bindings
));
371 if (bindings
== NULL
)
372 errx(1, "out of memory");
374 sockaddr_to_gss_address (myctladdr
,
375 &bindings
->initiator_addrtype
,
376 &bindings
->initiator_address
);
377 sockaddr_to_gss_address (hisctladdr
,
378 &bindings
->acceptor_addrtype
,
379 &bindings
->acceptor_address
);
381 bindings
->application_data
.length
= 0;
382 bindings
->application_data
.value
= NULL
;
384 bindings
= GSS_C_NO_CHANNEL_BINDINGS
;
386 if (ftp_do_gss_delegate
)
387 mech_flags
|= GSS_C_DELEG_FLAG
;
389 while(!context_established
) {
390 maj_stat
= gss_init_sec_context(&min_stat
,
403 if (GSS_ERROR(maj_stat
)) {
405 OM_uint32 msg_ctx
= 0;
406 gss_buffer_desc status_string
;
408 d
->context_hdl
= GSS_C_NO_CONTEXT
;
410 gss_release_name(&min_stat
, &target_name
);
414 if(import_name(*kname
++, host
, &target_name
)) {
415 if (bindings
!= GSS_C_NO_CHANNEL_BINDINGS
)
422 if (bindings
!= GSS_C_NO_CHANNEL_BINDINGS
)
425 gss_display_status(&new_stat
,
431 printf("Error initializing security context: %s\n",
432 (char*)status_string
.value
);
433 gss_release_buffer(&new_stat
, &status_string
);
434 return AUTH_CONTINUE
;
442 if (output_token
.length
!= 0) {
443 base64_encode(output_token
.value
, output_token
.length
, &p
);
444 gss_release_buffer(&min_stat
, &output_token
);
445 n
= command("ADAT %s", p
);
448 if (GSS_ERROR(maj_stat
)) {
449 if (d
->context_hdl
!= GSS_C_NO_CONTEXT
)
450 gss_delete_sec_context (&min_stat
,
455 if (maj_stat
& GSS_S_CONTINUE_NEEDED
) {
456 p
= strstr(reply_string
, "ADAT=");
458 printf("Error: expected ADAT in reply. got: %s\n",
460 if (bindings
!= GSS_C_NO_CHANNEL_BINDINGS
)
465 input
.value
= malloc(strlen(p
));
466 input
.length
= base64_decode(p
, input
.value
);
470 printf("Unrecognized response code: %d\n", code
);
471 if (bindings
!= GSS_C_NO_CHANNEL_BINDINGS
)
475 context_established
= 1;
479 gss_release_name(&min_stat
, &target_name
);
481 if (bindings
!= GSS_C_NO_CHANNEL_BINDINGS
)
487 gss_name_t targ_name
;
489 maj_stat
= gss_inquire_context(&min_stat
,
498 if (GSS_ERROR(maj_stat
) == 0) {
499 gss_buffer_desc name
;
500 maj_stat
= gss_display_name (&min_stat
,
504 if (GSS_ERROR(maj_stat
) == 0) {
505 printf("Authenticated to <%s>\n", (char *)name
.value
);
506 gss_release_buffer(&min_stat
, &name
);
508 gss_release_name(&min_stat
, &targ_name
);
510 printf("Failed to get gss name of peer.\n");
517 struct sec_client_mech gss_client_mech
= {
519 sizeof(struct gss_data
),
529 #endif /* FTP_SERVER */