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