1 /* 2 * Copyright (c) 1998 - 2005 Kungliga Tekniska H�gskolan 3 * (Royal Institute of Technology, Stockholm, Sweden). 4 * All rights reserved. 5 * 6 * Redistribution and use in source and binary forms, with or without 7 * modification, are permitted provided that the following conditions 8 * are met: 9 * 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 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. 16 * 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. 20 * 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. 32 */ 33 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> 41 42 RCSID("$Id: gssapi.c 21513 2007-07-12 12:45:25Z lha $"); 43 44 int ftp_do_gss_bindings = 0; 45 int ftp_do_gss_delegate = 1; 46 47 struct gss_data { 48 gss_ctx_id_t context_hdl; 49 char *client_name; 50 gss_cred_id_t delegated_cred_handle; 51 void *mech_data; 52 }; 53 54 static int 55 gss_init(void *app_data) 56 { 57 struct gss_data *d = app_data; 58 d->context_hdl = GSS_C_NO_CONTEXT; 59 d->delegated_cred_handle = GSS_C_NO_CREDENTIAL; 60 #if defined(FTP_SERVER) 61 return 0; 62 #else 63 /* XXX Check the gss mechanism; with gss_indicate_mechs() ? */ 64 #ifdef KRB5 65 return !use_kerberos; 66 #else 67 return 0; 68 #endif /* KRB5 */ 69 #endif /* FTP_SERVER */ 70 } 71 72 static int 73 gss_check_prot(void *app_data, int level) 74 { 75 if(level == prot_confidential) 76 return -1; 77 return 0; 78 } 79 80 static int 81 gss_decode(void *app_data, void *buf, int len, int level) 82 { 83 OM_uint32 maj_stat, min_stat; 84 gss_buffer_desc input, output; 85 gss_qop_t qop_state; 86 int conf_state; 87 struct gss_data *d = app_data; 88 size_t ret_len; 89 90 input.length = len; 91 input.value = buf; 92 maj_stat = gss_unwrap (&min_stat, 93 d->context_hdl, 94 &input, 95 &output, 96 &conf_state, 97 &qop_state); 98 if(GSS_ERROR(maj_stat)) 99 return -1; 100 memmove(buf, output.value, output.length); 101 ret_len = output.length; 102 gss_release_buffer(&min_stat, &output); 103 return ret_len; 104 } 105 106 static int 107 gss_overhead(void *app_data, int level, int len) 108 { 109 return 100; /* dunno? */ 110 } 111 112 113 static int 114 gss_encode(void *app_data, void *from, int length, int level, void **to) 115 { 116 OM_uint32 maj_stat, min_stat; 117 gss_buffer_desc input, output; 118 int conf_state; 119 struct gss_data *d = app_data; 120 121 input.length = length; 122 input.value = from; 123 maj_stat = gss_wrap (&min_stat, 124 d->context_hdl, 125 level == prot_private, 126 GSS_C_QOP_DEFAULT, 127 &input, 128 &conf_state, 129 &output); 130 *to = output.value; 131 return output.length; 132 } 133 134 static void 135 sockaddr_to_gss_address (struct sockaddr *sa, 136 OM_uint32 *addr_type, 137 gss_buffer_desc *gss_addr) 138 { 139 switch (sa->sa_family) { 140 #ifdef HAVE_IPV6 141 case AF_INET6 : { 142 struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)sa; 143 144 gss_addr->length = 16; 145 gss_addr->value = &sin6->sin6_addr; 146 *addr_type = GSS_C_AF_INET6; 147 break; 148 } 149 #endif 150 case AF_INET : { 151 struct sockaddr_in *sin4 = (struct sockaddr_in *)sa; 152 153 gss_addr->length = 4; 154 gss_addr->value = &sin4->sin_addr; 155 *addr_type = GSS_C_AF_INET; 156 break; 157 } 158 default : 159 errx (1, "unknown address family %d", sa->sa_family); 160 161 } 162 } 163 164 /* end common stuff */ 165 166 #ifdef FTP_SERVER 167 168 static int 169 gss_adat(void *app_data, void *buf, size_t len) 170 { 171 char *p = NULL; 172 gss_buffer_desc input_token, output_token; 173 OM_uint32 maj_stat, min_stat; 174 gss_name_t client_name; 175 struct gss_data *d = app_data; 176 gss_channel_bindings_t bindings; 177 178 if (ftp_do_gss_bindings) { 179 bindings = malloc(sizeof(*bindings)); 180 if (bindings == NULL) 181 errx(1, "out of memory"); 182 183 sockaddr_to_gss_address (his_addr, 184 &bindings->initiator_addrtype, 185 &bindings->initiator_address); 186 sockaddr_to_gss_address (ctrl_addr, 187 &bindings->acceptor_addrtype, 188 &bindings->acceptor_address); 189 190 bindings->application_data.length = 0; 191 bindings->application_data.value = NULL; 192 } else 193 bindings = GSS_C_NO_CHANNEL_BINDINGS; 194 195 input_token.value = buf; 196 input_token.length = len; 197 198 maj_stat = gss_accept_sec_context (&min_stat, 199 &d->context_hdl, 200 GSS_C_NO_CREDENTIAL, 201 &input_token, 202 bindings, 203 &client_name, 204 NULL, 205 &output_token, 206 NULL, 207 NULL, 208 &d->delegated_cred_handle); 209 210 if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 211 free(bindings); 212 213 if(output_token.length) { 214 if(base64_encode(output_token.value, output_token.length, &p) < 0) { 215 reply(535, "Out of memory base64-encoding."); 216 return -1; 217 } 218 gss_release_buffer(&min_stat, &output_token); 219 } 220 if(maj_stat == GSS_S_COMPLETE){ 221 char *name; 222 gss_buffer_desc export_name; 223 gss_OID oid; 224 225 maj_stat = gss_display_name(&min_stat, client_name, 226 &export_name, &oid); 227 if(maj_stat != 0) { 228 reply(500, "Error displaying name"); 229 goto out; 230 } 231 /* XXX kerberos */ 232 if(oid != GSS_KRB5_NT_PRINCIPAL_NAME) { 233 reply(500, "OID not kerberos principal name"); 234 gss_release_buffer(&min_stat, &export_name); 235 goto out; 236 } 237 name = malloc(export_name.length + 1); 238 if(name == NULL) { 239 reply(500, "Out of memory"); 240 gss_release_buffer(&min_stat, &export_name); 241 goto out; 242 } 243 memcpy(name, export_name.value, export_name.length); 244 name[export_name.length] = '\0'; 245 gss_release_buffer(&min_stat, &export_name); 246 d->client_name = name; 247 if(p) 248 reply(235, "ADAT=%s", p); 249 else 250 reply(235, "ADAT Complete"); 251 sec_complete = 1; 252 253 } else if(maj_stat == GSS_S_CONTINUE_NEEDED) { 254 if(p) 255 reply(335, "ADAT=%s", p); 256 else 257 reply(335, "OK, need more data"); 258 } else { 259 OM_uint32 new_stat; 260 OM_uint32 msg_ctx = 0; 261 gss_buffer_desc status_string; 262 gss_display_status(&new_stat, 263 min_stat, 264 GSS_C_MECH_CODE, 265 GSS_C_NO_OID, 266 &msg_ctx, 267 &status_string); 268 syslog(LOG_ERR, "gss_accept_sec_context: %s", 269 (char*)status_string.value); 270 gss_release_buffer(&new_stat, &status_string); 271 reply(431, "Security resource unavailable"); 272 } 273 out: 274 if (client_name) 275 gss_release_name(&min_stat, &client_name); 276 free(p); 277 return 0; 278 } 279 280 int gss_userok(void*, char*); 281 int gss_session(void*, char*); 282 283 struct sec_server_mech gss_server_mech = { 284 "GSSAPI", 285 sizeof(struct gss_data), 286 gss_init, /* init */ 287 NULL, /* end */ 288 gss_check_prot, 289 gss_overhead, 290 gss_encode, 291 gss_decode, 292 /* */ 293 NULL, 294 gss_adat, 295 NULL, /* pbsz */ 296 NULL, /* ccc */ 297 gss_userok, 298 gss_session 299 }; 300 301 #else /* FTP_SERVER */ 302 303 extern struct sockaddr *hisctladdr, *myctladdr; 304 305 static int 306 import_name(const char *kname, const char *host, gss_name_t *target_name) 307 { 308 OM_uint32 maj_stat, min_stat; 309 gss_buffer_desc name; 310 char *str; 311 312 name.length = asprintf(&str, "%s@%s", kname, host); 313 if (str == NULL) { 314 printf("Out of memory\n"); 315 return AUTH_ERROR; 316 } 317 name.value = str; 318 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; 327 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 free(name.value); 338 gss_release_buffer(&new_stat, &status_string); 339 return AUTH_ERROR; 340 } 341 free(name.value); 342 return 0; 343 } 344 345 static int 346 gss_auth(void *app_data, char *host) 347 { 348 349 OM_uint32 maj_stat, min_stat; 350 gss_name_t target_name; 351 gss_buffer_desc input, output_token; 352 int context_established = 0; 353 char *p; 354 int n; 355 gss_channel_bindings_t bindings; 356 struct gss_data *d = app_data; 357 OM_uint32 mech_flags = GSS_C_MUTUAL_FLAG | GSS_C_SEQUENCE_FLAG; 358 359 const char *knames[] = { "ftp", "host", NULL }, **kname = knames; 360 361 362 if(import_name(*kname++, host, &target_name)) 363 return AUTH_ERROR; 364 365 input.length = 0; 366 input.value = NULL; 367 368 if (ftp_do_gss_bindings) { 369 bindings = malloc(sizeof(*bindings)); 370 if (bindings == NULL) 371 errx(1, "out of memory"); 372 373 sockaddr_to_gss_address (myctladdr, 374 &bindings->initiator_addrtype, 375 &bindings->initiator_address); 376 sockaddr_to_gss_address (hisctladdr, 377 &bindings->acceptor_addrtype, 378 &bindings->acceptor_address); 379 380 bindings->application_data.length = 0; 381 bindings->application_data.value = NULL; 382 } else 383 bindings = GSS_C_NO_CHANNEL_BINDINGS; 384 385 if (ftp_do_gss_delegate) 386 mech_flags |= GSS_C_DELEG_FLAG; 387 388 while(!context_established) { 389 maj_stat = gss_init_sec_context(&min_stat, 390 GSS_C_NO_CREDENTIAL, 391 &d->context_hdl, 392 target_name, 393 GSS_C_NO_OID, 394 mech_flags, 395 0, 396 bindings, 397 &input, 398 NULL, 399 &output_token, 400 NULL, 401 NULL); 402 if (GSS_ERROR(maj_stat)) { 403 OM_uint32 new_stat; 404 OM_uint32 msg_ctx = 0; 405 gss_buffer_desc status_string; 406 407 d->context_hdl = GSS_C_NO_CONTEXT; 408 409 gss_release_name(&min_stat, &target_name); 410 411 if(*kname != NULL) { 412 413 if(import_name(*kname++, host, &target_name)) { 414 if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 415 free(bindings); 416 return AUTH_ERROR; 417 } 418 continue; 419 } 420 421 if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 422 free(bindings); 423 424 gss_display_status(&new_stat, 425 min_stat, 426 GSS_C_MECH_CODE, 427 GSS_C_NO_OID, 428 &msg_ctx, 429 &status_string); 430 printf("Error initializing security context: %s\n", 431 (char*)status_string.value); 432 gss_release_buffer(&new_stat, &status_string); 433 return AUTH_CONTINUE; 434 } 435 436 if (input.value) { 437 free(input.value); 438 input.value = NULL; 439 input.length = 0; 440 } 441 if (output_token.length != 0) { 442 base64_encode(output_token.value, output_token.length, &p); 443 gss_release_buffer(&min_stat, &output_token); 444 n = command("ADAT %s", p); 445 free(p); 446 } 447 if (GSS_ERROR(maj_stat)) { 448 if (d->context_hdl != GSS_C_NO_CONTEXT) 449 gss_delete_sec_context (&min_stat, 450 &d->context_hdl, 451 GSS_C_NO_BUFFER); 452 break; 453 } 454 if (maj_stat & GSS_S_CONTINUE_NEEDED) { 455 p = strstr(reply_string, "ADAT="); 456 if(p == NULL){ 457 printf("Error: expected ADAT in reply. got: %s\n", 458 reply_string); 459 if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 460 free(bindings); 461 return AUTH_ERROR; 462 } else { 463 p+=5; 464 input.value = malloc(strlen(p)); 465 input.length = base64_decode(p, input.value); 466 } 467 } else { 468 if(code != 235) { 469 printf("Unrecognized response code: %d\n", code); 470 if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 471 free(bindings); 472 return AUTH_ERROR; 473 } 474 context_established = 1; 475 } 476 } 477 478 gss_release_name(&min_stat, &target_name); 479 480 if (bindings != GSS_C_NO_CHANNEL_BINDINGS) 481 free(bindings); 482 if (input.value) 483 free(input.value); 484 485 { 486 gss_name_t targ_name; 487 488 maj_stat = gss_inquire_context(&min_stat, 489 d->context_hdl, 490 NULL, 491 &targ_name, 492 NULL, 493 NULL, 494 NULL, 495 NULL, 496 NULL); 497 if (GSS_ERROR(maj_stat) == 0) { 498 gss_buffer_desc name; 499 maj_stat = gss_display_name (&min_stat, 500 targ_name, 501 &name, 502 NULL); 503 if (GSS_ERROR(maj_stat) == 0) { 504 printf("Authenticated to <%s>\n", (char *)name.value); 505 gss_release_buffer(&min_stat, &name); 506 } 507 gss_release_name(&min_stat, &targ_name); 508 } else 509 printf("Failed to get gss name of peer.\n"); 510 } 511 512 513 return AUTH_OK; 514 } 515 516 struct sec_client_mech gss_client_mech = { 517 "GSSAPI", 518 sizeof(struct gss_data), 519 gss_init, 520 gss_auth, 521 NULL, /* end */ 522 gss_check_prot, 523 gss_overhead, 524 gss_encode, 525 gss_decode, 526 }; 527 528 #endif /* FTP_SERVER */ 529