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