1178825Sdfr/* 2233294Sstas * Copyright (c) 1998 - 2005 Kungliga Tekniska H��gskolan 3233294Sstas * (Royal Institute of Technology, Stockholm, Sweden). 4233294Sstas * All rights reserved. 5178825Sdfr * 6233294Sstas * Redistribution and use in source and binary forms, with or without 7233294Sstas * modification, are permitted provided that the following conditions 8233294Sstas * are met: 9178825Sdfr * 10233294Sstas * 1. Redistributions of source code must retain the above copyright 11233294Sstas * notice, this list of conditions and the following disclaimer. 12178825Sdfr * 13233294Sstas * 2. Redistributions in binary form must reproduce the above copyright 14233294Sstas * notice, this list of conditions and the following disclaimer in the 15233294Sstas * documentation and/or other materials provided with the distribution. 16178825Sdfr * 17233294Sstas * 3. Neither the name of the Institute nor the names of its contributors 18233294Sstas * may be used to endorse or promote products derived from this software 19233294Sstas * without specific prior written permission. 20178825Sdfr * 21233294Sstas * THIS SOFTWARE IS PROVIDED BY THE INSTITUTE AND CONTRIBUTORS ``AS IS'' AND 22233294Sstas * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 23233294Sstas * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 24233294Sstas * ARE DISCLAIMED. IN NO EVENT SHALL THE INSTITUTE OR CONTRIBUTORS BE LIABLE 25233294Sstas * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 26233294Sstas * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 27233294Sstas * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 28233294Sstas * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 29233294Sstas * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 30233294Sstas * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 31233294Sstas * SUCH DAMAGE. 32178825Sdfr */ 33178825Sdfr 34178825Sdfr#ifdef FTP_SERVER 35178825Sdfr#include "ftpd_locl.h" 36178825Sdfr#else 37178825Sdfr#include "ftp_locl.h" 38178825Sdfr#endif 39233294Sstas#include <gssapi/gssapi.h> 40233294Sstas#include <gssapi/gssapi_krb5.h> 41178825Sdfr#include <krb5_err.h> 42178825Sdfr 43233294SstasRCSID("$Id$"); 44178825Sdfr 45178825Sdfrint ftp_do_gss_bindings = 0; 46178825Sdfrint ftp_do_gss_delegate = 1; 47178825Sdfr 48233294Sstasstruct gssapi_data { 49178825Sdfr gss_ctx_id_t context_hdl; 50233294Sstas gss_name_t client_name; 51178825Sdfr gss_cred_id_t delegated_cred_handle; 52178825Sdfr void *mech_data; 53178825Sdfr}; 54178825Sdfr 55178825Sdfrstatic int 56178825Sdfrgss_init(void *app_data) 57178825Sdfr{ 58233294Sstas struct gssapi_data *d = app_data; 59178825Sdfr d->context_hdl = GSS_C_NO_CONTEXT; 60178825Sdfr d->delegated_cred_handle = GSS_C_NO_CREDENTIAL; 61178825Sdfr#if defined(FTP_SERVER) 62178825Sdfr return 0; 63178825Sdfr#else 64178825Sdfr /* XXX Check the gss mechanism; with gss_indicate_mechs() ? */ 65178825Sdfr#ifdef KRB5 66178825Sdfr return !use_kerberos; 67178825Sdfr#else 68178825Sdfr return 0; 69178825Sdfr#endif /* KRB5 */ 70178825Sdfr#endif /* FTP_SERVER */ 71178825Sdfr} 72178825Sdfr 73178825Sdfrstatic int 74178825Sdfrgss_check_prot(void *app_data, int level) 75178825Sdfr{ 76178825Sdfr if(level == prot_confidential) 77178825Sdfr return -1; 78178825Sdfr return 0; 79178825Sdfr} 80178825Sdfr 81178825Sdfrstatic int 82178825Sdfrgss_decode(void *app_data, void *buf, int len, int level) 83178825Sdfr{ 84178825Sdfr OM_uint32 maj_stat, min_stat; 85178825Sdfr gss_buffer_desc input, output; 86178825Sdfr gss_qop_t qop_state; 87178825Sdfr int conf_state; 88233294Sstas struct gssapi_data *d = app_data; 89178825Sdfr size_t ret_len; 90178825Sdfr 91178825Sdfr input.length = len; 92178825Sdfr input.value = buf; 93178825Sdfr maj_stat = gss_unwrap (&min_stat, 94178825Sdfr d->context_hdl, 95178825Sdfr &input, 96178825Sdfr &output, 97178825Sdfr &conf_state, 98178825Sdfr &qop_state); 99178825Sdfr if(GSS_ERROR(maj_stat)) 100178825Sdfr return -1; 101178825Sdfr memmove(buf, output.value, output.length); 102178825Sdfr ret_len = output.length; 103178825Sdfr gss_release_buffer(&min_stat, &output); 104178825Sdfr return ret_len; 105178825Sdfr} 106178825Sdfr 107178825Sdfrstatic int 108178825Sdfrgss_overhead(void *app_data, int level, int len) 109178825Sdfr{ 110178825Sdfr return 100; /* dunno? */ 111178825Sdfr} 112178825Sdfr 113178825Sdfr 114178825Sdfrstatic int 115178825Sdfrgss_encode(void *app_data, void *from, int length, int level, void **to) 116178825Sdfr{ 117178825Sdfr OM_uint32 maj_stat, min_stat; 118178825Sdfr gss_buffer_desc input, output; 119178825Sdfr int conf_state; 120233294Sstas struct gssapi_data *d = app_data; 121178825Sdfr 122178825Sdfr input.length = length; 123178825Sdfr input.value = from; 124178825Sdfr maj_stat = gss_wrap (&min_stat, 125178825Sdfr d->context_hdl, 126178825Sdfr level == prot_private, 127178825Sdfr GSS_C_QOP_DEFAULT, 128178825Sdfr &input, 129178825Sdfr &conf_state, 130178825Sdfr &output); 131178825Sdfr *to = output.value; 132178825Sdfr return output.length; 133178825Sdfr} 134178825Sdfr 135178825Sdfrstatic void 136178825Sdfrsockaddr_to_gss_address (struct sockaddr *sa, 137178825Sdfr OM_uint32 *addr_type, 138178825Sdfr gss_buffer_desc *gss_addr) 139178825Sdfr{ 140178825Sdfr switch (sa->sa_family) { 141178825Sdfr#ifdef HAVE_IPV6 142178825Sdfr case AF_INET6 : { 143178825Sdfr struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 144178825Sdfr 145178825Sdfr gss_addr->length = 16; 146178825Sdfr gss_addr->value = &sin6->sin6_addr; 147178825Sdfr *addr_type = GSS_C_AF_INET6; 148178825Sdfr break; 149178825Sdfr } 150178825Sdfr#endif 151178825Sdfr case AF_INET : { 152178825Sdfr struct sockaddr_in *sin4 = (struct sockaddr_in *)sa; 153178825Sdfr 154178825Sdfr gss_addr->length = 4; 155178825Sdfr gss_addr->value = &sin4->sin_addr; 156178825Sdfr *addr_type = GSS_C_AF_INET; 157178825Sdfr break; 158178825Sdfr } 159178825Sdfr default : 160178825Sdfr errx (1, "unknown address family %d", sa->sa_family); 161233294Sstas 162178825Sdfr } 163178825Sdfr} 164178825Sdfr 165178825Sdfr/* end common stuff */ 166178825Sdfr 167178825Sdfr#ifdef FTP_SERVER 168178825Sdfr 169178825Sdfrstatic int 170178825Sdfrgss_adat(void *app_data, void *buf, size_t len) 171178825Sdfr{ 172178825Sdfr char *p = NULL; 173178825Sdfr gss_buffer_desc input_token, output_token; 174178825Sdfr OM_uint32 maj_stat, min_stat; 175178825Sdfr gss_name_t client_name; 176233294Sstas struct gssapi_data *d = app_data; 177178825Sdfr gss_channel_bindings_t bindings; 178178825Sdfr 179178825Sdfr if (ftp_do_gss_bindings) { 180178825Sdfr bindings = malloc(sizeof(*bindings)); 181178825Sdfr if (bindings == NULL) 182178825Sdfr errx(1, "out of memory"); 183178825Sdfr 184178825Sdfr sockaddr_to_gss_address (his_addr, 185178825Sdfr &bindings->initiator_addrtype, 186178825Sdfr &bindings->initiator_address); 187178825Sdfr sockaddr_to_gss_address (ctrl_addr, 188178825Sdfr &bindings->acceptor_addrtype, 189178825Sdfr &bindings->acceptor_address); 190233294Sstas 191178825Sdfr bindings->application_data.length = 0; 192178825Sdfr bindings->application_data.value = NULL; 193178825Sdfr } else 194178825Sdfr bindings = GSS_C_NO_CHANNEL_BINDINGS; 195178825Sdfr 196178825Sdfr input_token.value = buf; 197178825Sdfr input_token.length = len; 198178825Sdfr 199178825Sdfr maj_stat = gss_accept_sec_context (&min_stat, 200178825Sdfr &d->context_hdl, 201178825Sdfr GSS_C_NO_CREDENTIAL, 202178825Sdfr &input_token, 203178825Sdfr bindings, 204178825Sdfr &client_name, 205178825Sdfr NULL, 206178825Sdfr &output_token, 207178825Sdfr NULL, 208178825Sdfr NULL, 209178825Sdfr &d->delegated_cred_handle); 210178825Sdfr 211178825Sdfr if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 212178825Sdfr free(bindings); 213178825Sdfr 214178825Sdfr if(output_token.length) { 215178825Sdfr if(base64_encode(output_token.value, output_token.length, &p) < 0) { 216178825Sdfr reply(535, "Out of memory base64-encoding."); 217178825Sdfr return -1; 218178825Sdfr } 219178825Sdfr gss_release_buffer(&min_stat, &output_token); 220178825Sdfr } 221178825Sdfr if(maj_stat == GSS_S_COMPLETE){ 222233294Sstas d->client_name = client_name; 223233294Sstas client_name = GSS_C_NO_NAME; 224178825Sdfr if(p) 225178825Sdfr reply(235, "ADAT=%s", p); 226178825Sdfr else 227178825Sdfr reply(235, "ADAT Complete"); 228178825Sdfr sec_complete = 1; 229178825Sdfr 230178825Sdfr } else if(maj_stat == GSS_S_CONTINUE_NEEDED) { 231178825Sdfr if(p) 232178825Sdfr reply(335, "ADAT=%s", p); 233178825Sdfr else 234178825Sdfr reply(335, "OK, need more data"); 235178825Sdfr } else { 236178825Sdfr OM_uint32 new_stat; 237178825Sdfr OM_uint32 msg_ctx = 0; 238178825Sdfr gss_buffer_desc status_string; 239178825Sdfr gss_display_status(&new_stat, 240178825Sdfr min_stat, 241178825Sdfr GSS_C_MECH_CODE, 242178825Sdfr GSS_C_NO_OID, 243178825Sdfr &msg_ctx, 244178825Sdfr &status_string); 245233294Sstas syslog(LOG_ERR, "gss_accept_sec_context: %.*s", 246233294Sstas (int)status_string.length, 247178825Sdfr (char*)status_string.value); 248178825Sdfr gss_release_buffer(&new_stat, &status_string); 249178825Sdfr reply(431, "Security resource unavailable"); 250178825Sdfr } 251233294Sstas 252178825Sdfr if (client_name) 253178825Sdfr gss_release_name(&min_stat, &client_name); 254178825Sdfr free(p); 255178825Sdfr return 0; 256178825Sdfr} 257178825Sdfr 258233294Sstasint gssapi_userok(void*, char*); 259233294Sstasint gssapi_session(void*, char*); 260178825Sdfr 261178825Sdfrstruct sec_server_mech gss_server_mech = { 262178825Sdfr "GSSAPI", 263233294Sstas sizeof(struct gssapi_data), 264178825Sdfr gss_init, /* init */ 265178825Sdfr NULL, /* end */ 266178825Sdfr gss_check_prot, 267178825Sdfr gss_overhead, 268178825Sdfr gss_encode, 269178825Sdfr gss_decode, 270178825Sdfr /* */ 271178825Sdfr NULL, 272178825Sdfr gss_adat, 273178825Sdfr NULL, /* pbsz */ 274178825Sdfr NULL, /* ccc */ 275233294Sstas gssapi_userok, 276233294Sstas gssapi_session 277178825Sdfr}; 278178825Sdfr 279178825Sdfr#else /* FTP_SERVER */ 280178825Sdfr 281178825Sdfrextern struct sockaddr *hisctladdr, *myctladdr; 282178825Sdfr 283178825Sdfrstatic int 284178825Sdfrimport_name(const char *kname, const char *host, gss_name_t *target_name) 285178825Sdfr{ 286178825Sdfr OM_uint32 maj_stat, min_stat; 287178825Sdfr gss_buffer_desc name; 288178825Sdfr char *str; 289178825Sdfr 290178825Sdfr name.length = asprintf(&str, "%s@%s", kname, host); 291178825Sdfr if (str == NULL) { 292178825Sdfr printf("Out of memory\n"); 293178825Sdfr return AUTH_ERROR; 294178825Sdfr } 295178825Sdfr name.value = str; 296178825Sdfr 297178825Sdfr maj_stat = gss_import_name(&min_stat, 298178825Sdfr &name, 299178825Sdfr GSS_C_NT_HOSTBASED_SERVICE, 300178825Sdfr target_name); 301178825Sdfr if (GSS_ERROR(maj_stat)) { 302178825Sdfr OM_uint32 new_stat; 303178825Sdfr OM_uint32 msg_ctx = 0; 304178825Sdfr gss_buffer_desc status_string; 305233294Sstas 306178825Sdfr gss_display_status(&new_stat, 307178825Sdfr min_stat, 308178825Sdfr GSS_C_MECH_CODE, 309178825Sdfr GSS_C_NO_OID, 310178825Sdfr &msg_ctx, 311178825Sdfr &status_string); 312233294Sstas printf("Error importing name %.*s: %.*s\n", 313233294Sstas (int)name.length, 314178825Sdfr (char *)name.value, 315233294Sstas (int)status_string.length, 316178825Sdfr (char *)status_string.value); 317178825Sdfr free(name.value); 318178825Sdfr gss_release_buffer(&new_stat, &status_string); 319178825Sdfr return AUTH_ERROR; 320178825Sdfr } 321178825Sdfr free(name.value); 322178825Sdfr return 0; 323178825Sdfr} 324178825Sdfr 325178825Sdfrstatic int 326178825Sdfrgss_auth(void *app_data, char *host) 327178825Sdfr{ 328233294Sstas 329178825Sdfr OM_uint32 maj_stat, min_stat; 330178825Sdfr gss_name_t target_name; 331178825Sdfr gss_buffer_desc input, output_token; 332178825Sdfr int context_established = 0; 333178825Sdfr char *p; 334178825Sdfr int n; 335178825Sdfr gss_channel_bindings_t bindings; 336233294Sstas struct gssapi_data *d = app_data; 337178825Sdfr OM_uint32 mech_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; 338178825Sdfr 339178825Sdfr const char *knames[] = { "ftp", "host", NULL }, **kname = knames; 340233294Sstas 341233294Sstas 342178825Sdfr if(import_name(*kname++, host, &target_name)) 343178825Sdfr return AUTH_ERROR; 344178825Sdfr 345178825Sdfr input.length = 0; 346178825Sdfr input.value = NULL; 347178825Sdfr 348178825Sdfr if (ftp_do_gss_bindings) { 349178825Sdfr bindings = malloc(sizeof(*bindings)); 350178825Sdfr if (bindings == NULL) 351178825Sdfr errx(1, "out of memory"); 352233294Sstas 353178825Sdfr sockaddr_to_gss_address (myctladdr, 354178825Sdfr &bindings->initiator_addrtype, 355178825Sdfr &bindings->initiator_address); 356178825Sdfr sockaddr_to_gss_address (hisctladdr, 357178825Sdfr &bindings->acceptor_addrtype, 358178825Sdfr &bindings->acceptor_address); 359233294Sstas 360178825Sdfr bindings->application_data.length = 0; 361178825Sdfr bindings->application_data.value = NULL; 362178825Sdfr } else 363178825Sdfr bindings = GSS_C_NO_CHANNEL_BINDINGS; 364178825Sdfr 365178825Sdfr if (ftp_do_gss_delegate) 366178825Sdfr mech_flags |= GSS_C_DELEG_FLAG; 367178825Sdfr 368178825Sdfr while(!context_established) { 369178825Sdfr maj_stat = gss_init_sec_context(&min_stat, 370178825Sdfr GSS_C_NO_CREDENTIAL, 371178825Sdfr &d->context_hdl, 372178825Sdfr target_name, 373178825Sdfr GSS_C_NO_OID, 374178825Sdfr mech_flags, 375178825Sdfr 0, 376178825Sdfr bindings, 377178825Sdfr &input, 378178825Sdfr NULL, 379178825Sdfr &output_token, 380178825Sdfr NULL, 381178825Sdfr NULL); 382178825Sdfr if (GSS_ERROR(maj_stat)) { 383178825Sdfr OM_uint32 new_stat; 384178825Sdfr OM_uint32 msg_ctx = 0; 385178825Sdfr gss_buffer_desc status_string; 386178825Sdfr 387178825Sdfr d->context_hdl = GSS_C_NO_CONTEXT; 388178825Sdfr 389178825Sdfr gss_release_name(&min_stat, &target_name); 390178825Sdfr 391178825Sdfr if(*kname != NULL) { 392178825Sdfr 393178825Sdfr if(import_name(*kname++, host, &target_name)) { 394178825Sdfr if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 395178825Sdfr free(bindings); 396178825Sdfr return AUTH_ERROR; 397178825Sdfr } 398178825Sdfr continue; 399178825Sdfr } 400233294Sstas 401178825Sdfr if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 402178825Sdfr free(bindings); 403178825Sdfr 404178825Sdfr gss_display_status(&new_stat, 405178825Sdfr min_stat, 406178825Sdfr GSS_C_MECH_CODE, 407178825Sdfr GSS_C_NO_OID, 408178825Sdfr &msg_ctx, 409178825Sdfr &status_string); 410233294Sstas printf("Error initializing security context: %.*s\n", 411233294Sstas (int)status_string.length, 412178825Sdfr (char*)status_string.value); 413178825Sdfr gss_release_buffer(&new_stat, &status_string); 414178825Sdfr return AUTH_CONTINUE; 415178825Sdfr } 416178825Sdfr 417178825Sdfr if (input.value) { 418178825Sdfr free(input.value); 419178825Sdfr input.value = NULL; 420178825Sdfr input.length = 0; 421178825Sdfr } 422178825Sdfr if (output_token.length != 0) { 423178825Sdfr base64_encode(output_token.value, output_token.length, &p); 424178825Sdfr gss_release_buffer(&min_stat, &output_token); 425178825Sdfr n = command("ADAT %s", p); 426178825Sdfr free(p); 427178825Sdfr } 428178825Sdfr if (GSS_ERROR(maj_stat)) { 429178825Sdfr if (d->context_hdl != GSS_C_NO_CONTEXT) 430178825Sdfr gss_delete_sec_context (&min_stat, 431178825Sdfr &d->context_hdl, 432178825Sdfr GSS_C_NO_BUFFER); 433178825Sdfr break; 434178825Sdfr } 435178825Sdfr if (maj_stat & GSS_S_CONTINUE_NEEDED) { 436178825Sdfr p = strstr(reply_string, "ADAT="); 437178825Sdfr if(p == NULL){ 438178825Sdfr printf("Error: expected ADAT in reply. got: %s\n", 439178825Sdfr reply_string); 440178825Sdfr if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 441178825Sdfr free(bindings); 442178825Sdfr return AUTH_ERROR; 443178825Sdfr } else { 444178825Sdfr p+=5; 445178825Sdfr input.value = malloc(strlen(p)); 446178825Sdfr input.length = base64_decode(p, input.value); 447178825Sdfr } 448178825Sdfr } else { 449178825Sdfr if(code != 235) { 450178825Sdfr printf("Unrecognized response code: %d\n", code); 451178825Sdfr if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 452178825Sdfr free(bindings); 453178825Sdfr return AUTH_ERROR; 454178825Sdfr } 455178825Sdfr context_established = 1; 456178825Sdfr } 457178825Sdfr } 458178825Sdfr 459178825Sdfr gss_release_name(&min_stat, &target_name); 460178825Sdfr 461178825Sdfr if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 462178825Sdfr free(bindings); 463178825Sdfr if (input.value) 464178825Sdfr free(input.value); 465178825Sdfr 466178825Sdfr { 467178825Sdfr gss_name_t targ_name; 468178825Sdfr 469178825Sdfr maj_stat = gss_inquire_context(&min_stat, 470178825Sdfr d->context_hdl, 471178825Sdfr NULL, 472178825Sdfr &targ_name, 473178825Sdfr NULL, 474178825Sdfr NULL, 475178825Sdfr NULL, 476178825Sdfr NULL, 477178825Sdfr NULL); 478178825Sdfr if (GSS_ERROR(maj_stat) == 0) { 479178825Sdfr gss_buffer_desc name; 480178825Sdfr maj_stat = gss_display_name (&min_stat, 481178825Sdfr targ_name, 482178825Sdfr &name, 483178825Sdfr NULL); 484178825Sdfr if (GSS_ERROR(maj_stat) == 0) { 485233294Sstas printf("Authenticated to <%.*s>\n", 486233294Sstas (int)name.length, 487233294Sstas (char *)name.value); 488178825Sdfr gss_release_buffer(&min_stat, &name); 489178825Sdfr } 490178825Sdfr gss_release_name(&min_stat, &targ_name); 491178825Sdfr } else 492178825Sdfr printf("Failed to get gss name of peer.\n"); 493233294Sstas } 494178825Sdfr 495178825Sdfr 496178825Sdfr return AUTH_OK; 497178825Sdfr} 498178825Sdfr 499178825Sdfrstruct sec_client_mech gss_client_mech = { 500178825Sdfr "GSSAPI", 501233294Sstas sizeof(struct gssapi_data), 502178825Sdfr gss_init, 503178825Sdfr gss_auth, 504178825Sdfr NULL, /* end */ 505178825Sdfr gss_check_prot, 506178825Sdfr gss_overhead, 507178825Sdfr gss_encode, 508178825Sdfr gss_decode, 509178825Sdfr}; 510178825Sdfr 511178825Sdfr#endif /* FTP_SERVER */ 512