1/* 2 Unix SMB/CIFS implementation. 3 4 Kerberos backend for GENSEC 5 6 Copyright (C) Andrew Bartlett <abartlet@samba.org> 2004 7 Copyright (C) Andrew Tridgell 2001 8 Copyright (C) Luke Howard 2002-2003 9 Copyright (C) Stefan Metzmacher 2004-2005 10 11 This program is free software; you can redistribute it and/or modify 12 it under the terms of the GNU General Public License as published by 13 the Free Software Foundation; either version 3 of the License, or 14 (at your option) any later version. 15 16 This program is distributed in the hope that it will be useful, 17 but WITHOUT ANY WARRANTY; without even the implied warranty of 18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 GNU General Public License for more details. 20 21 22 You should have received a copy of the GNU General Public License 23 along with this program. If not, see <http://www.gnu.org/licenses/>. 24*/ 25 26#include "includes.h" 27#include "system/kerberos.h" 28#include "auth/kerberos/kerberos.h" 29#include "librpc/gen_ndr/krb5pac.h" 30#include "auth/auth.h" 31#include "lib/ldb/include/ldb.h" 32#include "auth/auth_sam.h" 33#include "lib/socket/socket.h" 34#include "librpc/rpc/dcerpc.h" 35#include "auth/credentials/credentials.h" 36#include "auth/credentials/credentials_krb5.h" 37#include "auth/gensec/gensec.h" 38#include "auth/gensec/gensec_proto.h" 39#include "param/param.h" 40#include "auth/session_proto.h" 41#include "auth/auth_sam_reply.h" 42 43enum GENSEC_KRB5_STATE { 44 GENSEC_KRB5_SERVER_START, 45 GENSEC_KRB5_CLIENT_START, 46 GENSEC_KRB5_CLIENT_MUTUAL_AUTH, 47 GENSEC_KRB5_DONE 48}; 49 50struct gensec_krb5_state { 51 DATA_BLOB session_key; 52 DATA_BLOB pac; 53 enum GENSEC_KRB5_STATE state_position; 54 struct smb_krb5_context *smb_krb5_context; 55 krb5_auth_context auth_context; 56 krb5_data enc_ticket; 57 krb5_keyblock *keyblock; 58 krb5_ticket *ticket; 59 bool gssapi; 60 krb5_flags ap_req_options; 61}; 62 63static int gensec_krb5_destroy(struct gensec_krb5_state *gensec_krb5_state) 64{ 65 if (!gensec_krb5_state->smb_krb5_context) { 66 /* We can't clean anything else up unless we started up this far */ 67 return 0; 68 } 69 if (gensec_krb5_state->enc_ticket.length) { 70 kerberos_free_data_contents(gensec_krb5_state->smb_krb5_context->krb5_context, 71 &gensec_krb5_state->enc_ticket); 72 } 73 74 if (gensec_krb5_state->ticket) { 75 krb5_free_ticket(gensec_krb5_state->smb_krb5_context->krb5_context, 76 gensec_krb5_state->ticket); 77 } 78 79 /* ccache freed in a child destructor */ 80 81 krb5_free_keyblock(gensec_krb5_state->smb_krb5_context->krb5_context, 82 gensec_krb5_state->keyblock); 83 84 if (gensec_krb5_state->auth_context) { 85 krb5_auth_con_free(gensec_krb5_state->smb_krb5_context->krb5_context, 86 gensec_krb5_state->auth_context); 87 } 88 89 return 0; 90} 91 92static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi) 93{ 94 krb5_error_code ret; 95 struct gensec_krb5_state *gensec_krb5_state; 96 struct cli_credentials *creds; 97 const struct socket_address *my_addr, *peer_addr; 98 krb5_address my_krb5_addr, peer_krb5_addr; 99 100 creds = gensec_get_credentials(gensec_security); 101 if (!creds) { 102 return NT_STATUS_INVALID_PARAMETER; 103 } 104 105 gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state); 106 if (!gensec_krb5_state) { 107 return NT_STATUS_NO_MEMORY; 108 } 109 110 gensec_security->private_data = gensec_krb5_state; 111 gensec_krb5_state->smb_krb5_context = NULL; 112 gensec_krb5_state->auth_context = NULL; 113 gensec_krb5_state->ticket = NULL; 114 ZERO_STRUCT(gensec_krb5_state->enc_ticket); 115 gensec_krb5_state->keyblock = NULL; 116 gensec_krb5_state->session_key = data_blob(NULL, 0); 117 gensec_krb5_state->pac = data_blob(NULL, 0); 118 gensec_krb5_state->gssapi = gssapi; 119 120 talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy); 121 122 if (cli_credentials_get_krb5_context(creds, 123 gensec_security->event_ctx, 124 gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) { 125 talloc_free(gensec_krb5_state); 126 return NT_STATUS_INTERNAL_ERROR; 127 } 128 129 ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context); 130 if (ret) { 131 DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", 132 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 133 ret, gensec_krb5_state))); 134 talloc_free(gensec_krb5_state); 135 return NT_STATUS_INTERNAL_ERROR; 136 } 137 138 ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context, 139 gensec_krb5_state->auth_context, 140 KRB5_AUTH_CONTEXT_DO_SEQUENCE); 141 if (ret) { 142 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n", 143 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 144 ret, gensec_krb5_state))); 145 talloc_free(gensec_krb5_state); 146 return NT_STATUS_INTERNAL_ERROR; 147 } 148 149 my_addr = gensec_get_my_addr(gensec_security); 150 if (my_addr && my_addr->sockaddr) { 151 ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context, 152 my_addr->sockaddr, &my_krb5_addr); 153 if (ret) { 154 DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", 155 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 156 ret, gensec_krb5_state))); 157 talloc_free(gensec_krb5_state); 158 return NT_STATUS_INTERNAL_ERROR; 159 } 160 } 161 162 peer_addr = gensec_get_peer_addr(gensec_security); 163 if (peer_addr && peer_addr->sockaddr) { 164 ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context, 165 peer_addr->sockaddr, &peer_krb5_addr); 166 if (ret) { 167 DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", 168 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 169 ret, gensec_krb5_state))); 170 talloc_free(gensec_krb5_state); 171 return NT_STATUS_INTERNAL_ERROR; 172 } 173 } 174 175 ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context, 176 gensec_krb5_state->auth_context, 177 my_addr ? &my_krb5_addr : NULL, 178 peer_addr ? &peer_krb5_addr : NULL); 179 if (ret) { 180 DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n", 181 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 182 ret, gensec_krb5_state))); 183 talloc_free(gensec_krb5_state); 184 return NT_STATUS_INTERNAL_ERROR; 185 } 186 187 return NT_STATUS_OK; 188} 189 190static NTSTATUS gensec_krb5_common_server_start(struct gensec_security *gensec_security, bool gssapi) 191{ 192 NTSTATUS nt_status; 193 struct gensec_krb5_state *gensec_krb5_state; 194 195 nt_status = gensec_krb5_start(gensec_security, gssapi); 196 if (!NT_STATUS_IS_OK(nt_status)) { 197 return nt_status; 198 } 199 200 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; 201 gensec_krb5_state->state_position = GENSEC_KRB5_SERVER_START; 202 203 return NT_STATUS_OK; 204} 205 206static NTSTATUS gensec_krb5_server_start(struct gensec_security *gensec_security) 207{ 208 return gensec_krb5_common_server_start(gensec_security, false); 209} 210 211static NTSTATUS gensec_fake_gssapi_krb5_server_start(struct gensec_security *gensec_security) 212{ 213 return gensec_krb5_common_server_start(gensec_security, true); 214} 215 216static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_security, bool gssapi) 217{ 218 struct gensec_krb5_state *gensec_krb5_state; 219 krb5_error_code ret; 220 NTSTATUS nt_status; 221 struct ccache_container *ccache_container; 222 const char *hostname; 223 224 const char *principal; 225 krb5_data in_data; 226 227 hostname = gensec_get_target_hostname(gensec_security); 228 if (!hostname) { 229 DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n")); 230 return NT_STATUS_INVALID_PARAMETER; 231 } 232 if (is_ipaddress(hostname)) { 233 DEBUG(2, ("Cannot do krb5 to an IP address")); 234 return NT_STATUS_INVALID_PARAMETER; 235 } 236 if (strcmp(hostname, "localhost") == 0) { 237 DEBUG(2, ("krb5 to 'localhost' does not make sense")); 238 return NT_STATUS_INVALID_PARAMETER; 239 } 240 241 nt_status = gensec_krb5_start(gensec_security, gssapi); 242 if (!NT_STATUS_IS_OK(nt_status)) { 243 return nt_status; 244 } 245 246 gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; 247 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START; 248 gensec_krb5_state->ap_req_options = AP_OPTS_USE_SUBKEY; 249 250 if (gensec_krb5_state->gssapi) { 251 /* The Fake GSSAPI modal emulates Samba3, which does not do mutual authentication */ 252 if (gensec_setting_bool(gensec_security->settings, "gensec_fake_gssapi_krb5", "mutual", false)) { 253 gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; 254 } 255 } else { 256 /* The wrapping for KPASSWD (a user of the raw KRB5 API) should be mutually authenticated */ 257 if (gensec_setting_bool(gensec_security->settings, "gensec_krb5", "mutual", true)) { 258 gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; 259 } 260 } 261 262 principal = gensec_get_target_principal(gensec_security); 263 264 ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), 265 gensec_security->event_ctx, 266 gensec_security->settings->lp_ctx, &ccache_container); 267 switch (ret) { 268 case 0: 269 break; 270 case KRB5KDC_ERR_PREAUTH_FAILED: 271 return NT_STATUS_LOGON_FAILURE; 272 case KRB5_KDC_UNREACH: 273 DEBUG(3, ("Cannot reach a KDC we require to contact %s\n", principal)); 274 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ 275 default: 276 DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_message(ret))); 277 return NT_STATUS_UNSUCCESSFUL; 278 } 279 in_data.length = 0; 280 281 if (principal && lp_client_use_spnego_principal(gensec_security->settings->lp_ctx)) { 282 krb5_principal target_principal; 283 ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal, 284 &target_principal); 285 if (ret == 0) { 286 ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context, 287 &gensec_krb5_state->auth_context, 288 gensec_krb5_state->ap_req_options, 289 target_principal, 290 &in_data, ccache_container->ccache, 291 &gensec_krb5_state->enc_ticket); 292 krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context, 293 target_principal); 294 } 295 } else { 296 ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context, 297 &gensec_krb5_state->auth_context, 298 gensec_krb5_state->ap_req_options, 299 gensec_get_target_service(gensec_security), 300 hostname, 301 &in_data, ccache_container->ccache, 302 &gensec_krb5_state->enc_ticket); 303 } 304 switch (ret) { 305 case 0: 306 return NT_STATUS_OK; 307 case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: 308 DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", 309 hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); 310 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ 311 case KRB5_KDC_UNREACH: 312 DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n", 313 hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); 314 return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ 315 case KRB5KDC_ERR_PREAUTH_FAILED: 316 case KRB5KRB_AP_ERR_TKT_EXPIRED: 317 case KRB5_CC_END: 318 /* Too much clock skew - we will need to kinit to re-skew the clock */ 319 case KRB5KRB_AP_ERR_SKEW: 320 case KRB5_KDCREP_SKEW: 321 { 322 DEBUG(3, ("kerberos (mk_req) failed: %s\n", 323 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); 324 /*fall through*/ 325 } 326 327 /* just don't print a message for these really ordinary messages */ 328 case KRB5_FCC_NOFILE: 329 case KRB5_CC_NOTFOUND: 330 case ENOENT: 331 332 return NT_STATUS_UNSUCCESSFUL; 333 break; 334 335 default: 336 DEBUG(0, ("kerberos: %s\n", 337 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); 338 return NT_STATUS_UNSUCCESSFUL; 339 } 340} 341 342static NTSTATUS gensec_krb5_client_start(struct gensec_security *gensec_security) 343{ 344 return gensec_krb5_common_client_start(gensec_security, false); 345} 346 347static NTSTATUS gensec_fake_gssapi_krb5_client_start(struct gensec_security *gensec_security) 348{ 349 return gensec_krb5_common_client_start(gensec_security, true); 350} 351 352/** 353 * Check if the packet is one for this mechansim 354 * 355 * @param gensec_security GENSEC state 356 * @param in The request, as a DATA_BLOB 357 * @return Error, INVALID_PARAMETER if it's not a packet for us 358 * or NT_STATUS_OK if the packet is ok. 359 */ 360 361static NTSTATUS gensec_fake_gssapi_krb5_magic(struct gensec_security *gensec_security, 362 const DATA_BLOB *in) 363{ 364 if (gensec_gssapi_check_oid(in, GENSEC_OID_KERBEROS5)) { 365 return NT_STATUS_OK; 366 } else { 367 return NT_STATUS_INVALID_PARAMETER; 368 } 369} 370 371 372/** 373 * Next state function for the Krb5 GENSEC mechanism 374 * 375 * @param gensec_krb5_state KRB5 State 376 * @param out_mem_ctx The TALLOC_CTX for *out to be allocated on 377 * @param in The request, as a DATA_BLOB 378 * @param out The reply, as an talloc()ed DATA_BLOB, on *out_mem_ctx 379 * @return Error, MORE_PROCESSING_REQUIRED if a reply is sent, 380 * or NT_STATUS_OK if the user is authenticated. 381 */ 382 383static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, 384 TALLOC_CTX *out_mem_ctx, 385 const DATA_BLOB in, DATA_BLOB *out) 386{ 387 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; 388 krb5_error_code ret = 0; 389 NTSTATUS nt_status; 390 391 switch (gensec_krb5_state->state_position) { 392 case GENSEC_KRB5_CLIENT_START: 393 { 394 DATA_BLOB unwrapped_out; 395 396 if (gensec_krb5_state->gssapi) { 397 unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length); 398 399 /* wrap that up in a nice GSS-API wrapping */ 400 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ); 401 } else { 402 *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length); 403 } 404 if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) { 405 gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH; 406 nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; 407 } else { 408 gensec_krb5_state->state_position = GENSEC_KRB5_DONE; 409 nt_status = NT_STATUS_OK; 410 } 411 return nt_status; 412 } 413 414 case GENSEC_KRB5_CLIENT_MUTUAL_AUTH: 415 { 416 DATA_BLOB unwrapped_in; 417 krb5_data inbuf; 418 krb5_ap_rep_enc_part *repl = NULL; 419 uint8_t tok_id[2]; 420 421 if (gensec_krb5_state->gssapi) { 422 if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { 423 DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n")); 424 dump_data_pw("Mutual authentication message:\n", in.data, in.length); 425 return NT_STATUS_INVALID_PARAMETER; 426 } 427 } else { 428 unwrapped_in = in; 429 } 430 /* TODO: check the tok_id */ 431 432 inbuf.data = unwrapped_in.data; 433 inbuf.length = unwrapped_in.length; 434 ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context, 435 gensec_krb5_state->auth_context, 436 &inbuf, &repl); 437 if (ret) { 438 DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n", 439 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx))); 440 dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length); 441 nt_status = NT_STATUS_ACCESS_DENIED; 442 } else { 443 *out = data_blob(NULL, 0); 444 nt_status = NT_STATUS_OK; 445 gensec_krb5_state->state_position = GENSEC_KRB5_DONE; 446 } 447 if (repl) { 448 krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl); 449 } 450 return nt_status; 451 } 452 453 case GENSEC_KRB5_SERVER_START: 454 { 455 DATA_BLOB unwrapped_in; 456 DATA_BLOB unwrapped_out = data_blob(NULL, 0); 457 krb5_data inbuf, outbuf; 458 uint8_t tok_id[2]; 459 struct keytab_container *keytab; 460 krb5_principal server_in_keytab; 461 462 if (!in.data) { 463 return NT_STATUS_INVALID_PARAMETER; 464 } 465 466 /* Grab the keytab, however generated */ 467 ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), 468 gensec_security->event_ctx, 469 gensec_security->settings->lp_ctx, &keytab); 470 if (ret) { 471 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 472 } 473 474 /* This ensures we lookup the correct entry in that keytab */ 475 ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security), 476 gensec_krb5_state->smb_krb5_context, 477 &server_in_keytab); 478 479 if (ret) { 480 return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; 481 } 482 483 /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */ 484 if (gensec_krb5_state->gssapi 485 && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { 486 inbuf.data = unwrapped_in.data; 487 inbuf.length = unwrapped_in.length; 488 } else { 489 inbuf.data = in.data; 490 inbuf.length = in.length; 491 } 492 493 ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context, 494 &gensec_krb5_state->auth_context, 495 &inbuf, keytab->keytab, server_in_keytab, 496 &outbuf, 497 &gensec_krb5_state->ticket, 498 &gensec_krb5_state->keyblock); 499 500 if (ret) { 501 return NT_STATUS_LOGON_FAILURE; 502 } 503 unwrapped_out.data = (uint8_t *)outbuf.data; 504 unwrapped_out.length = outbuf.length; 505 gensec_krb5_state->state_position = GENSEC_KRB5_DONE; 506 /* wrap that up in a nice GSS-API wrapping */ 507 if (gensec_krb5_state->gssapi) { 508 *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP); 509 } else { 510 *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length); 511 } 512 krb5_data_free(&outbuf); 513 return NT_STATUS_OK; 514 } 515 516 case GENSEC_KRB5_DONE: 517 default: 518 /* Asking too many times... */ 519 return NT_STATUS_INVALID_PARAMETER; 520 } 521} 522 523static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security, 524 DATA_BLOB *session_key) 525{ 526 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; 527 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; 528 krb5_auth_context auth_context = gensec_krb5_state->auth_context; 529 krb5_keyblock *skey; 530 krb5_error_code err = -1; 531 532 if (gensec_krb5_state->state_position != GENSEC_KRB5_DONE) { 533 return NT_STATUS_NO_USER_SESSION_KEY; 534 } 535 536 if (gensec_krb5_state->session_key.data) { 537 *session_key = gensec_krb5_state->session_key; 538 return NT_STATUS_OK; 539 } 540 541 switch (gensec_security->gensec_role) { 542 case GENSEC_CLIENT: 543 err = krb5_auth_con_getlocalsubkey(context, auth_context, &skey); 544 break; 545 case GENSEC_SERVER: 546 err = krb5_auth_con_getremotesubkey(context, auth_context, &skey); 547 break; 548 } 549 if (err == 0 && skey != NULL) { 550 DEBUG(10, ("Got KRB5 session key of length %d\n", 551 (int)KRB5_KEY_LENGTH(skey))); 552 gensec_krb5_state->session_key = data_blob_talloc(gensec_krb5_state, 553 KRB5_KEY_DATA(skey), KRB5_KEY_LENGTH(skey)); 554 *session_key = gensec_krb5_state->session_key; 555 dump_data_pw("KRB5 Session Key:\n", session_key->data, session_key->length); 556 557 krb5_free_keyblock(context, skey); 558 return NT_STATUS_OK; 559 } else { 560 DEBUG(10, ("KRB5 error getting session key %d\n", err)); 561 return NT_STATUS_NO_USER_SESSION_KEY; 562 } 563} 564 565static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security, 566 struct auth_session_info **_session_info) 567{ 568 NTSTATUS nt_status = NT_STATUS_UNSUCCESSFUL; 569 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; 570 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; 571 struct auth_serversupplied_info *server_info = NULL; 572 struct auth_session_info *session_info = NULL; 573 struct PAC_LOGON_INFO *logon_info; 574 575 krb5_principal client_principal; 576 char *principal_string; 577 578 DATA_BLOB pac; 579 krb5_data pac_data; 580 581 krb5_error_code ret; 582 583 TALLOC_CTX *mem_ctx = talloc_new(gensec_security); 584 if (!mem_ctx) { 585 return NT_STATUS_NO_MEMORY; 586 } 587 588 ret = krb5_ticket_get_client(context, gensec_krb5_state->ticket, &client_principal); 589 if (ret) { 590 DEBUG(5, ("krb5_ticket_get_client failed to get cleint principal: %s\n", 591 smb_get_krb5_error_message(context, 592 ret, mem_ctx))); 593 talloc_free(mem_ctx); 594 return NT_STATUS_NO_MEMORY; 595 } 596 597 ret = krb5_unparse_name(gensec_krb5_state->smb_krb5_context->krb5_context, 598 client_principal, &principal_string); 599 if (ret) { 600 DEBUG(1, ("Unable to parse client principal: %s\n", 601 smb_get_krb5_error_message(context, 602 ret, mem_ctx))); 603 talloc_free(mem_ctx); 604 return NT_STATUS_NO_MEMORY; 605 } 606 607 ret = krb5_ticket_get_authorization_data_type(context, gensec_krb5_state->ticket, 608 KRB5_AUTHDATA_WIN2K_PAC, 609 &pac_data); 610 611 if (ret && gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) { 612 DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access: %s \n", 613 principal_string, 614 smb_get_krb5_error_message(context, 615 ret, mem_ctx))); 616 krb5_free_principal(context, client_principal); 617 free(principal_string); 618 return NT_STATUS_ACCESS_DENIED; 619 } else if (ret) { 620 /* NO pac */ 621 DEBUG(5, ("krb5_ticket_get_authorization_data_type failed to find PAC: %s\n", 622 smb_get_krb5_error_message(context, 623 ret, mem_ctx))); 624 if (gensec_security->auth_context && 625 !gensec_setting_bool(gensec_security->settings, "gensec", "require_pac", false)) { 626 DEBUG(1, ("Unable to find PAC for %s, resorting to local user lookup: %s", 627 principal_string, smb_get_krb5_error_message(context, 628 ret, mem_ctx))); 629 nt_status = gensec_security->auth_context->get_server_info_principal(mem_ctx, 630 gensec_security->auth_context, 631 principal_string, 632 &server_info); 633 if (!NT_STATUS_IS_OK(nt_status)) { 634 talloc_free(mem_ctx); 635 return nt_status; 636 } 637 } else { 638 DEBUG(1, ("Unable to find PAC in ticket from %s, failing to allow access\n", 639 principal_string)); 640 return NT_STATUS_ACCESS_DENIED; 641 } 642 643 krb5_free_principal(context, client_principal); 644 free(principal_string); 645 646 if (!NT_STATUS_IS_OK(nt_status)) { 647 talloc_free(mem_ctx); 648 return nt_status; 649 } 650 } else { 651 /* Found pac */ 652 union netr_Validation validation; 653 free(principal_string); 654 655 pac = data_blob_talloc(mem_ctx, pac_data.data, pac_data.length); 656 if (!pac.data) { 657 krb5_free_principal(context, client_principal); 658 talloc_free(mem_ctx); 659 return NT_STATUS_NO_MEMORY; 660 } 661 662 /* decode and verify the pac */ 663 nt_status = kerberos_pac_logon_info(gensec_krb5_state, 664 gensec_security->settings->iconv_convenience, 665 &logon_info, pac, 666 gensec_krb5_state->smb_krb5_context->krb5_context, 667 NULL, gensec_krb5_state->keyblock, 668 client_principal, 669 gensec_krb5_state->ticket->ticket.authtime, NULL); 670 krb5_free_principal(context, client_principal); 671 672 if (!NT_STATUS_IS_OK(nt_status)) { 673 talloc_free(mem_ctx); 674 return nt_status; 675 } 676 677 validation.sam3 = &logon_info->info3; 678 nt_status = make_server_info_netlogon_validation(mem_ctx, 679 NULL, 680 3, &validation, 681 &server_info); 682 if (!NT_STATUS_IS_OK(nt_status)) { 683 talloc_free(mem_ctx); 684 return nt_status; 685 } 686 } 687 688 /* references the server_info into the session_info */ 689 nt_status = auth_generate_session_info(mem_ctx, gensec_security->event_ctx, gensec_security->settings->lp_ctx, server_info, &session_info); 690 691 if (!NT_STATUS_IS_OK(nt_status)) { 692 talloc_free(mem_ctx); 693 return nt_status; 694 } 695 696 nt_status = gensec_krb5_session_key(gensec_security, &session_info->session_key); 697 698 if (!NT_STATUS_IS_OK(nt_status)) { 699 talloc_free(mem_ctx); 700 return nt_status; 701 } 702 703 *_session_info = session_info; 704 705 talloc_steal(gensec_krb5_state, session_info); 706 talloc_free(mem_ctx); 707 return NT_STATUS_OK; 708} 709 710static NTSTATUS gensec_krb5_wrap(struct gensec_security *gensec_security, 711 TALLOC_CTX *mem_ctx, 712 const DATA_BLOB *in, 713 DATA_BLOB *out) 714{ 715 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; 716 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; 717 krb5_auth_context auth_context = gensec_krb5_state->auth_context; 718 krb5_error_code ret; 719 krb5_data input, output; 720 input.length = in->length; 721 input.data = in->data; 722 723 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { 724 ret = krb5_mk_priv(context, auth_context, &input, &output, NULL); 725 if (ret) { 726 DEBUG(1, ("krb5_mk_priv failed: %s\n", 727 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 728 ret, mem_ctx))); 729 return NT_STATUS_ACCESS_DENIED; 730 } 731 *out = data_blob_talloc(mem_ctx, output.data, output.length); 732 733 krb5_data_free(&output); 734 } else { 735 return NT_STATUS_ACCESS_DENIED; 736 } 737 return NT_STATUS_OK; 738} 739 740static NTSTATUS gensec_krb5_unwrap(struct gensec_security *gensec_security, 741 TALLOC_CTX *mem_ctx, 742 const DATA_BLOB *in, 743 DATA_BLOB *out) 744{ 745 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; 746 krb5_context context = gensec_krb5_state->smb_krb5_context->krb5_context; 747 krb5_auth_context auth_context = gensec_krb5_state->auth_context; 748 krb5_error_code ret; 749 krb5_data input, output; 750 krb5_replay_data replay; 751 input.length = in->length; 752 input.data = in->data; 753 754 if (gensec_have_feature(gensec_security, GENSEC_FEATURE_SEAL)) { 755 ret = krb5_rd_priv(context, auth_context, &input, &output, &replay); 756 if (ret) { 757 DEBUG(1, ("krb5_rd_priv failed: %s\n", 758 smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, 759 ret, mem_ctx))); 760 return NT_STATUS_ACCESS_DENIED; 761 } 762 *out = data_blob_talloc(mem_ctx, output.data, output.length); 763 764 krb5_data_free(&output); 765 } else { 766 return NT_STATUS_ACCESS_DENIED; 767 } 768 return NT_STATUS_OK; 769} 770 771static bool gensec_krb5_have_feature(struct gensec_security *gensec_security, 772 uint32_t feature) 773{ 774 struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; 775 if (feature & GENSEC_FEATURE_SESSION_KEY) { 776 return true; 777 } 778 if (!gensec_krb5_state->gssapi && 779 (feature & GENSEC_FEATURE_SEAL)) { 780 return true; 781 } 782 783 return false; 784} 785 786static const char *gensec_krb5_oids[] = { 787 GENSEC_OID_KERBEROS5, 788 GENSEC_OID_KERBEROS5_OLD, 789 NULL 790}; 791 792static const struct gensec_security_ops gensec_fake_gssapi_krb5_security_ops = { 793 .name = "fake_gssapi_krb5", 794 .auth_type = DCERPC_AUTH_TYPE_KRB5, 795 .oid = gensec_krb5_oids, 796 .client_start = gensec_fake_gssapi_krb5_client_start, 797 .server_start = gensec_fake_gssapi_krb5_server_start, 798 .update = gensec_krb5_update, 799 .magic = gensec_fake_gssapi_krb5_magic, 800 .session_key = gensec_krb5_session_key, 801 .session_info = gensec_krb5_session_info, 802 .have_feature = gensec_krb5_have_feature, 803 .enabled = false, 804 .kerberos = true, 805 .priority = GENSEC_KRB5 806}; 807 808static const struct gensec_security_ops gensec_krb5_security_ops = { 809 .name = "krb5", 810 .client_start = gensec_krb5_client_start, 811 .server_start = gensec_krb5_server_start, 812 .update = gensec_krb5_update, 813 .session_key = gensec_krb5_session_key, 814 .session_info = gensec_krb5_session_info, 815 .have_feature = gensec_krb5_have_feature, 816 .wrap = gensec_krb5_wrap, 817 .unwrap = gensec_krb5_unwrap, 818 .enabled = true, 819 .kerberos = true, 820 .priority = GENSEC_KRB5 821}; 822 823_PUBLIC_ NTSTATUS gensec_krb5_init(void) 824{ 825 NTSTATUS ret; 826 827 ret = gensec_register(&gensec_krb5_security_ops); 828 if (!NT_STATUS_IS_OK(ret)) { 829 DEBUG(0,("Failed to register '%s' gensec backend!\n", 830 gensec_krb5_security_ops.name)); 831 return ret; 832 } 833 834 ret = gensec_register(&gensec_fake_gssapi_krb5_security_ops); 835 if (!NT_STATUS_IS_OK(ret)) { 836 DEBUG(0,("Failed to register '%s' gensec backend!\n", 837 gensec_krb5_security_ops.name)); 838 return ret; 839 } 840 841 return ret; 842} 843