static NTSTATUS gensec_gssapi_server_start(struct gensec_security *gensec_security) { NTSTATUS nt_status; int ret; struct gensec_gssapi_state *gensec_gssapi_state; struct cli_credentials *machine_account; struct gssapi_creds_container *gcc; nt_status = gensec_gssapi_start(gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); machine_account = gensec_get_credentials(gensec_security); if (!machine_account) { DEBUG(3, ("No machine account credentials specified\n")); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } else { ret = cli_credentials_get_server_gss_creds(machine_account, gensec_security->settings->lp_ctx, &gcc); if (ret) { DEBUG(1, ("Aquiring acceptor credentials failed: %s\n", error_message(ret))); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } } gensec_gssapi_state->server_cred = gcc; return NT_STATUS_OK; }
static NTSTATUS gensec_gssapi_client_creds(struct gensec_security *gensec_security, struct tevent_context *ev) { struct gensec_gssapi_state *gensec_gssapi_state; struct gssapi_creds_container *gcc; struct cli_credentials *creds = gensec_get_credentials(gensec_security); const char *error_string; int ret; gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); /* Only run this the first time the update() call is made */ if (gensec_gssapi_state->client_cred) { return NT_STATUS_OK; } ret = cli_credentials_get_client_gss_creds(creds, ev, gensec_security->settings->lp_ctx, &gcc, &error_string); switch (ret) { case 0: break; case EINVAL: DEBUG(3, ("Cannot obtain client GSS credentials we need to contact %s : %s\n", gensec_gssapi_state->target_principal, error_string)); return NT_STATUS_INVALID_PARAMETER; case KRB5KDC_ERR_PREAUTH_FAILED: case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: case KRB5KRB_AP_ERR_BAD_INTEGRITY: DEBUG(1, ("Wrong username or password: %s\n", error_string)); return NT_STATUS_LOGON_FAILURE; case KRB5KDC_ERR_CLIENT_REVOKED: DEBUG(1, ("Account locked out: %s\n", error_string)); return NT_STATUS_ACCOUNT_LOCKED_OUT; case KRB5_REALM_UNKNOWN: case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require to contact %s : %s\n", gensec_gssapi_state->target_principal, error_string)); return NT_STATUS_NO_LOGON_SERVERS; case KRB5_CC_NOTFOUND: case KRB5_CC_END: DEBUG(2, ("Error obtaining ticket we require to contact %s: (possibly due to clock skew between us and the KDC) %s\n", gensec_gssapi_state->target_principal, error_string)); return NT_STATUS_TIME_DIFFERENCE_AT_DC; default: DEBUG(1, ("Aquiring initiator credentials failed: %s\n", error_string)); return NT_STATUS_UNSUCCESSFUL; } gensec_gssapi_state->client_cred = gcc; if (!talloc_reference(gensec_gssapi_state, gcc)) { return NT_STATUS_NO_MEMORY; } return NT_STATUS_OK; }
static void manage_gensec_request(enum stdio_helper_mode stdio_helper_mode, struct loadparm_context *lp_ctx, char *buf, int length, void **private1, unsigned int mux_id, void **private2) { DATA_BLOB in; DATA_BLOB out = data_blob(NULL, 0); char *out_base64 = NULL; const char *reply_arg = NULL; struct gensec_ntlm_state { struct gensec_security *gensec_state; const char *set_password; }; struct gensec_ntlm_state *state; struct tevent_context *ev; struct imessaging_context *msg; NTSTATUS nt_status; bool first = false; const char *reply_code; struct cli_credentials *creds; static char *want_feature_list = NULL; static DATA_BLOB session_key; TALLOC_CTX *mem_ctx; if (*private1) { state = (struct gensec_ntlm_state *)*private1; } else { state = talloc_zero(NULL, struct gensec_ntlm_state); if (!state) { mux_printf(mux_id, "BH No Memory\n"); exit(1); } *private1 = state; if (opt_password) { state->set_password = opt_password; } } if (strlen(buf) < 2) { DEBUG(1, ("query [%s] invalid", buf)); mux_printf(mux_id, "BH Query invalid\n"); return; } if (strlen(buf) > 3) { if(strncmp(buf, "SF ", 3) == 0) { DEBUG(10, ("Setting flags to negotiate\n")); talloc_free(want_feature_list); want_feature_list = talloc_strndup(state, buf+3, strlen(buf)-3); mux_printf(mux_id, "OK\n"); return; } in = base64_decode_data_blob(buf + 3); } else { in = data_blob(NULL, 0); } if (strncmp(buf, "YR", 2) == 0) { if (state->gensec_state) { talloc_free(state->gensec_state); state->gensec_state = NULL; } } else if ( (strncmp(buf, "OK", 2) == 0)) { /* Just return BH, like ntlm_auth from Samba 3 does. */ mux_printf(mux_id, "BH Command expected\n"); data_blob_free(&in); return; } else if ( (strncmp(buf, "TT ", 3) != 0) && (strncmp(buf, "KK ", 3) != 0) && (strncmp(buf, "AF ", 3) != 0) && (strncmp(buf, "NA ", 3) != 0) && (strncmp(buf, "UG", 2) != 0) && (strncmp(buf, "PW ", 3) != 0) && (strncmp(buf, "GK", 2) != 0) && (strncmp(buf, "GF", 2) != 0)) { DEBUG(1, ("SPNEGO request [%s] invalid\n", buf)); mux_printf(mux_id, "BH SPNEGO request invalid\n"); data_blob_free(&in); return; } ev = s4_event_context_init(state); if (!ev) { exit(1); } mem_ctx = talloc_named(NULL, 0, "manage_gensec_request internal mem_ctx"); /* setup gensec */ if (!(state->gensec_state)) { switch (stdio_helper_mode) { case GSS_SPNEGO_CLIENT: case NTLMSSP_CLIENT_1: /* setup the client side */ nt_status = gensec_client_start(NULL, &state->gensec_state, ev, lpcfg_gensec_settings(NULL, lp_ctx)); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); exit(1); } break; case GSS_SPNEGO_SERVER: case SQUID_2_5_NTLMSSP: { const char *winbind_method[] = { "winbind", NULL }; struct auth4_context *auth_context; msg = imessaging_client_init(state, lpcfg_imessaging_path(state, lp_ctx), ev); if (!msg) { talloc_free(mem_ctx); exit(1); } nt_status = auth_context_create_methods(mem_ctx, winbind_method, ev, msg, lp_ctx, NULL, &auth_context); if (!NT_STATUS_IS_OK(nt_status)) { talloc_free(mem_ctx); exit(1); } if (!NT_STATUS_IS_OK(gensec_server_start(state, ev, lpcfg_gensec_settings(state, lp_ctx), auth_context, &state->gensec_state))) { talloc_free(mem_ctx); exit(1); } break; } default: talloc_free(mem_ctx); abort(); } creds = cli_credentials_init(state->gensec_state); cli_credentials_set_conf(creds, lp_ctx); if (opt_username) { cli_credentials_set_username(creds, opt_username, CRED_SPECIFIED); } if (opt_domain) { cli_credentials_set_domain(creds, opt_domain, CRED_SPECIFIED); } if (state->set_password) { cli_credentials_set_password(creds, state->set_password, CRED_SPECIFIED); } else { cli_credentials_set_password_callback(creds, get_password); creds->priv_data = (void*)(uintptr_t)mux_id; } if (opt_workstation) { cli_credentials_set_workstation(creds, opt_workstation, CRED_SPECIFIED); } switch (stdio_helper_mode) { case GSS_SPNEGO_SERVER: case SQUID_2_5_NTLMSSP: cli_credentials_set_machine_account(creds, lp_ctx); break; default: break; } gensec_set_credentials(state->gensec_state, creds); gensec_want_feature_list(state->gensec_state, want_feature_list); switch (stdio_helper_mode) { case GSS_SPNEGO_CLIENT: case GSS_SPNEGO_SERVER: nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_SPNEGO); if (!in.length) { first = true; } break; case NTLMSSP_CLIENT_1: if (!in.length) { first = true; } /* fall through */ case SQUID_2_5_NTLMSSP: nt_status = gensec_start_mech_by_oid(state->gensec_state, GENSEC_OID_NTLMSSP); break; default: talloc_free(mem_ctx); abort(); } if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("GENSEC mech failed to start: %s\n", nt_errstr(nt_status))); mux_printf(mux_id, "BH GENSEC mech failed to start\n"); talloc_free(mem_ctx); return; } } /* update */ if (strncmp(buf, "PW ", 3) == 0) { state->set_password = talloc_strndup(state, (const char *)in.data, in.length); cli_credentials_set_password(gensec_get_credentials(state->gensec_state), state->set_password, CRED_SPECIFIED); mux_printf(mux_id, "OK\n"); data_blob_free(&in); talloc_free(mem_ctx); return; } if (strncmp(buf, "UG", 2) == 0) { int i; char *grouplist = NULL; struct auth_session_info *session_info; nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info); if (!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("gensec_session_info failed: %s\n", nt_errstr(nt_status))); mux_printf(mux_id, "BH %s\n", nt_errstr(nt_status)); data_blob_free(&in); talloc_free(mem_ctx); return; } /* get the string onto the context */ grouplist = talloc_strdup(mem_ctx, ""); for (i=0; i<session_info->security_token->num_sids; i++) { struct security_token *token = session_info->security_token; const char *sidstr = dom_sid_string(session_info, &token->sids[i]); grouplist = talloc_asprintf_append_buffer(grouplist, "%s,", sidstr); } mux_printf(mux_id, "GL %s\n", grouplist); talloc_free(session_info); data_blob_free(&in); talloc_free(mem_ctx); return; } if (strncmp(buf, "GK", 2) == 0) { char *base64_key; DEBUG(10, ("Requested session key\n")); nt_status = gensec_session_key(state->gensec_state, mem_ctx, &session_key); if(!NT_STATUS_IS_OK(nt_status)) { DEBUG(1, ("gensec_session_key failed: %s\n", nt_errstr(nt_status))); mux_printf(mux_id, "BH No session key\n"); talloc_free(mem_ctx); return; } else { base64_key = base64_encode_data_blob(state, session_key); mux_printf(mux_id, "GK %s\n", base64_key); talloc_free(base64_key); } talloc_free(mem_ctx); return; } if (strncmp(buf, "GF", 2) == 0) { struct ntlmssp_state *ntlmssp_state; uint32_t neg_flags; ntlmssp_state = talloc_get_type(state->gensec_state->private_data, struct ntlmssp_state); neg_flags = ntlmssp_state->neg_flags; DEBUG(10, ("Requested negotiated feature flags\n")); mux_printf(mux_id, "GF 0x%08x\n", neg_flags); return; } nt_status = gensec_update(state->gensec_state, mem_ctx, in, &out); /* don't leak 'bad password'/'no such user' info to the network client */ nt_status = nt_status_squash(nt_status); if (out.length) { out_base64 = base64_encode_data_blob(mem_ctx, out); } else { out_base64 = NULL; } if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { reply_arg = "*"; if (first) { reply_code = "YR"; } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) { reply_code = "KK"; } else if (state->gensec_state->gensec_role == GENSEC_SERVER) { reply_code = "TT"; } else { abort(); } } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_ACCESS_DENIED)) { reply_code = "BH NT_STATUS_ACCESS_DENIED"; reply_arg = nt_errstr(nt_status); DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_UNSUCCESSFUL)) { reply_code = "BH NT_STATUS_UNSUCCESSFUL"; reply_arg = nt_errstr(nt_status); DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); } else if (!NT_STATUS_IS_OK(nt_status)) { reply_code = "NA"; reply_arg = nt_errstr(nt_status); DEBUG(1, ("GENSEC login failed: %s\n", nt_errstr(nt_status))); } else if /* OK */ (state->gensec_state->gensec_role == GENSEC_SERVER) { struct auth_session_info *session_info; nt_status = gensec_session_info(state->gensec_state, mem_ctx, &session_info); if (!NT_STATUS_IS_OK(nt_status)) { reply_code = "BH Failed to retrive session info"; reply_arg = nt_errstr(nt_status); DEBUG(1, ("GENSEC failed to retrieve the session info: %s\n", nt_errstr(nt_status))); } else { reply_code = "AF"; reply_arg = talloc_asprintf(state->gensec_state, "%s%s%s", session_info->info->domain_name, lpcfg_winbind_separator(lp_ctx), session_info->info->account_name); talloc_free(session_info); } } else if (state->gensec_state->gensec_role == GENSEC_CLIENT) { reply_code = "AF"; reply_arg = out_base64; } else { abort(); } switch (stdio_helper_mode) { case GSS_SPNEGO_SERVER: mux_printf(mux_id, "%s %s %s\n", reply_code, out_base64 ? out_base64 : "*", reply_arg ? reply_arg : "*"); break; default: if (out_base64) { mux_printf(mux_id, "%s %s\n", reply_code, out_base64); } else if (reply_arg) { mux_printf(mux_id, "%s %s\n", reply_code, reply_arg); } else { mux_printf(mux_id, "%s\n", reply_code); } } talloc_free(mem_ctx); return; }
static NTSTATUS gensec_gssapi_client_start(struct gensec_security *gensec_security) { struct gensec_gssapi_state *gensec_gssapi_state; struct cli_credentials *creds = gensec_get_credentials(gensec_security); NTSTATUS nt_status; gss_buffer_desc name_token; gss_OID name_type; OM_uint32 maj_stat, min_stat; const char *hostname = gensec_get_target_hostname(gensec_security); if (!hostname) { DEBUG(3, ("No hostname for target computer passed in, cannot use kerberos for this connection\n")); return NT_STATUS_INVALID_PARAMETER; } if (is_ipaddress(hostname)) { DEBUG(2, ("Cannot do GSSAPI to an IP address\n")); return NT_STATUS_INVALID_PARAMETER; } if (strcmp(hostname, "localhost") == 0) { DEBUG(2, ("GSSAPI to 'localhost' does not make sense\n")); return NT_STATUS_INVALID_PARAMETER; } nt_status = gensec_gssapi_start(gensec_security); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } gensec_gssapi_state = talloc_get_type(gensec_security->private_data, struct gensec_gssapi_state); if (cli_credentials_get_impersonate_principal(creds)) { gensec_gssapi_state->gss_want_flags &= ~(GSS_C_DELEG_FLAG|GSS_C_DELEG_POLICY_FLAG); } gensec_gssapi_state->target_principal = gensec_get_target_principal(gensec_security); if (gensec_gssapi_state->target_principal) { name_type = GSS_C_NULL_OID; } else { gensec_gssapi_state->target_principal = talloc_asprintf(gensec_gssapi_state, "%s/%s@%s", gensec_get_target_service(gensec_security), hostname, cli_credentials_get_realm(creds)); name_type = GSS_C_NT_USER_NAME; } name_token.value = discard_const_p(uint8_t, gensec_gssapi_state->target_principal); name_token.length = strlen(gensec_gssapi_state->target_principal); maj_stat = gss_import_name (&min_stat, &name_token, name_type, &gensec_gssapi_state->server_name); if (maj_stat) { DEBUG(2, ("GSS Import name of %s failed: %s\n", (char *)name_token.value, gssapi_error_string(gensec_gssapi_state, maj_stat, min_stat, gensec_gssapi_state->gss_oid))); return NT_STATUS_INVALID_PARAMETER; } return NT_STATUS_OK; }
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi) { krb5_error_code ret; struct gensec_krb5_state *gensec_krb5_state; struct cli_credentials *creds; const struct tsocket_address *tlocal_addr, *tremote_addr; krb5_address my_krb5_addr, peer_krb5_addr; creds = gensec_get_credentials(gensec_security); if (!creds) { return NT_STATUS_INVALID_PARAMETER; } gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state); if (!gensec_krb5_state) { return NT_STATUS_NO_MEMORY; } gensec_security->private_data = gensec_krb5_state; gensec_krb5_state->smb_krb5_context = NULL; gensec_krb5_state->auth_context = NULL; gensec_krb5_state->ticket = NULL; ZERO_STRUCT(gensec_krb5_state->enc_ticket); gensec_krb5_state->keyblock = NULL; gensec_krb5_state->gssapi = gssapi; talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy); if (cli_credentials_get_krb5_context(creds, gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) { talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } tlocal_addr = gensec_get_local_address(gensec_security); if (tlocal_addr) { ssize_t socklen; struct sockaddr_storage ss; socklen = tsocket_address_bsd_sockaddr(tlocal_addr, (struct sockaddr *) &ss, sizeof(struct sockaddr_storage)); if (socklen < 0) { talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context, (const struct sockaddr *) &ss, &my_krb5_addr); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } } tremote_addr = gensec_get_remote_address(gensec_security); if (tremote_addr) { ssize_t socklen; struct sockaddr_storage ss; socklen = tsocket_address_bsd_sockaddr(tremote_addr, (struct sockaddr *) &ss, sizeof(struct sockaddr_storage)); if (socklen < 0) { talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context, (const struct sockaddr *) &ss, &peer_krb5_addr); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } } ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->auth_context, tlocal_addr ? &my_krb5_addr : NULL, tremote_addr ? &peer_krb5_addr : NULL); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } return NT_STATUS_OK; }
static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALLOC_CTX *out_mem_ctx, struct tevent_context *ev, const DATA_BLOB in, DATA_BLOB *out) { struct gensec_krb5_state *gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; krb5_error_code ret = 0; NTSTATUS nt_status; switch (gensec_krb5_state->state_position) { case GENSEC_KRB5_CLIENT_START: { DATA_BLOB unwrapped_out; nt_status = gensec_krb5_common_client_creds(gensec_security, ev, gensec_krb5_state->gssapi); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } if (gensec_krb5_state->gssapi) { unwrapped_out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length); /* wrap that up in a nice GSS-API wrapping */ *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REQ); } else { *out = data_blob_talloc(out_mem_ctx, gensec_krb5_state->enc_ticket.data, gensec_krb5_state->enc_ticket.length); } if (gensec_krb5_state->ap_req_options & AP_OPTS_MUTUAL_REQUIRED) { gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_MUTUAL_AUTH; nt_status = NT_STATUS_MORE_PROCESSING_REQUIRED; } else { gensec_krb5_state->state_position = GENSEC_KRB5_DONE; nt_status = NT_STATUS_OK; } return nt_status; } case GENSEC_KRB5_CLIENT_MUTUAL_AUTH: { DATA_BLOB unwrapped_in; krb5_data inbuf; krb5_ap_rep_enc_part *repl = NULL; uint8_t tok_id[2]; if (gensec_krb5_state->gssapi) { if (!gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { DEBUG(1,("gensec_gssapi_parse_krb5_wrap(mutual authentication) failed to parse\n")); dump_data_pw("Mutual authentication message:\n", in.data, in.length); return NT_STATUS_INVALID_PARAMETER; } } else { unwrapped_in = in; } /* TODO: check the tok_id */ inbuf.data = unwrapped_in.data; inbuf.length = unwrapped_in.length; ret = krb5_rd_rep(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->auth_context, &inbuf, &repl); if (ret) { DEBUG(1,("krb5_rd_rep (mutual authentication) failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, out_mem_ctx))); dump_data_pw("Mutual authentication message:\n", (uint8_t *)inbuf.data, inbuf.length); nt_status = NT_STATUS_ACCESS_DENIED; } else { *out = data_blob(NULL, 0); nt_status = NT_STATUS_OK; gensec_krb5_state->state_position = GENSEC_KRB5_DONE; } if (repl) { krb5_free_ap_rep_enc_part(gensec_krb5_state->smb_krb5_context->krb5_context, repl); } return nt_status; } case GENSEC_KRB5_SERVER_START: { DATA_BLOB unwrapped_in; DATA_BLOB unwrapped_out = data_blob(NULL, 0); krb5_data inbuf, outbuf; uint8_t tok_id[2]; struct keytab_container *keytab; krb5_principal server_in_keytab; const char *error_string; enum credentials_obtained obtained; if (!in.data) { return NT_STATUS_INVALID_PARAMETER; } /* Grab the keytab, however generated */ ret = cli_credentials_get_keytab(gensec_get_credentials(gensec_security), gensec_security->settings->lp_ctx, &keytab); if (ret) { return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } /* This ensures we lookup the correct entry in that keytab */ ret = principal_from_credentials(out_mem_ctx, gensec_get_credentials(gensec_security), gensec_krb5_state->smb_krb5_context, &server_in_keytab, &obtained, &error_string); if (ret) { DEBUG(2,("Failed to make credentials from principal: %s\n", error_string)); return NT_STATUS_CANT_ACCESS_DOMAIN_INFO; } /* Parse the GSSAPI wrapping, if it's there... (win2k3 allows it to be omited) */ if (gensec_krb5_state->gssapi && gensec_gssapi_parse_krb5_wrap(out_mem_ctx, &in, &unwrapped_in, tok_id)) { inbuf.data = unwrapped_in.data; inbuf.length = unwrapped_in.length; } else { inbuf.data = in.data; inbuf.length = in.length; } ret = smb_rd_req_return_stuff(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context, &inbuf, keytab->keytab, server_in_keytab, &outbuf, &gensec_krb5_state->ticket, &gensec_krb5_state->keyblock); if (ret) { return NT_STATUS_LOGON_FAILURE; } unwrapped_out.data = (uint8_t *)outbuf.data; unwrapped_out.length = outbuf.length; gensec_krb5_state->state_position = GENSEC_KRB5_DONE; /* wrap that up in a nice GSS-API wrapping */ if (gensec_krb5_state->gssapi) { *out = gensec_gssapi_gen_krb5_wrap(out_mem_ctx, &unwrapped_out, TOK_ID_KRB_AP_REP); } else { *out = data_blob_talloc(out_mem_ctx, outbuf.data, outbuf.length); } krb5_data_free(&outbuf); return NT_STATUS_OK; } case GENSEC_KRB5_DONE: default: /* Asking too many times... */ return NT_STATUS_INVALID_PARAMETER; } }
static NTSTATUS gensec_krb5_common_client_creds(struct gensec_security *gensec_security, struct tevent_context *ev, bool gssapi) { struct gensec_krb5_state *gensec_krb5_state; krb5_error_code ret; struct ccache_container *ccache_container; const char *error_string; const char *principal; const char *hostname; krb5_data in_data; struct tevent_context *previous_ev; gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; principal = gensec_get_target_principal(gensec_security); hostname = gensec_get_target_hostname(gensec_security); ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), ev, gensec_security->settings->lp_ctx, &ccache_container, &error_string); switch (ret) { case 0: break; case KRB5KDC_ERR_PREAUTH_FAILED: case KRB5KDC_ERR_C_PRINCIPAL_UNKNOWN: return NT_STATUS_LOGON_FAILURE; case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require to contact %s: %s\n", principal, error_string)); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5_CC_NOTFOUND: case KRB5_CC_END: DEBUG(3, ("Error preparing credentials we require to contact %s : %s\n", principal, error_string)); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ default: DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_string)); return NT_STATUS_UNSUCCESSFUL; } in_data.length = 0; /* Do this every time, in case we have weird recursive issues here */ ret = smb_krb5_context_set_event_ctx(gensec_krb5_state->smb_krb5_context, ev, &previous_ev); if (ret != 0) { DEBUG(1, ("gensec_krb5_start: Setting event context failed\n")); return NT_STATUS_NO_MEMORY; } if (principal) { krb5_principal target_principal; ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal, &target_principal); if (ret == 0) { ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context, gensec_krb5_state->ap_req_options, target_principal, &in_data, ccache_container->ccache, &gensec_krb5_state->enc_ticket); krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context, target_principal); } } else { ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context, gensec_krb5_state->ap_req_options, gensec_get_target_service(gensec_security), hostname, &in_data, ccache_container->ccache, &gensec_krb5_state->enc_ticket); } smb_krb5_context_remove_event_ctx(gensec_krb5_state->smb_krb5_context, previous_ev, ev); switch (ret) { case 0: return NT_STATUS_OK; case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n", hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KDC_ERR_PREAUTH_FAILED: case KRB5KRB_AP_ERR_TKT_EXPIRED: case KRB5_CC_END: /* Too much clock skew - we will need to kinit to re-skew the clock */ case KRB5KRB_AP_ERR_SKEW: case KRB5_KDCREP_SKEW: { DEBUG(3, ("kerberos (mk_req) failed: %s\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); /*fall through*/ } /* just don't print a message for these really ordinary messages */ case KRB5_FCC_NOFILE: case KRB5_CC_NOTFOUND: case ENOENT: return NT_STATUS_UNSUCCESSFUL; break; default: DEBUG(0, ("kerberos: %s\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); return NT_STATUS_UNSUCCESSFUL; } }
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security, bool gssapi) { krb5_error_code ret; struct gensec_krb5_state *gensec_krb5_state; struct cli_credentials *creds; const struct socket_address *my_addr, *peer_addr; krb5_address my_krb5_addr, peer_krb5_addr; creds = gensec_get_credentials(gensec_security); if (!creds) { return NT_STATUS_INVALID_PARAMETER; } gensec_krb5_state = talloc(gensec_security, struct gensec_krb5_state); if (!gensec_krb5_state) { return NT_STATUS_NO_MEMORY; } gensec_security->private_data = gensec_krb5_state; gensec_krb5_state->smb_krb5_context = NULL; gensec_krb5_state->auth_context = NULL; gensec_krb5_state->ticket = NULL; ZERO_STRUCT(gensec_krb5_state->enc_ticket); gensec_krb5_state->keyblock = NULL; gensec_krb5_state->session_key = data_blob(NULL, 0); gensec_krb5_state->pac = data_blob(NULL, 0); gensec_krb5_state->gssapi = gssapi; talloc_set_destructor(gensec_krb5_state, gensec_krb5_destroy); if (cli_credentials_get_krb5_context(creds, gensec_security->event_ctx, gensec_security->settings->lp_ctx, &gensec_krb5_state->smb_krb5_context)) { talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } ret = krb5_auth_con_init(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_auth_con_init failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } ret = krb5_auth_con_setflags(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->auth_context, KRB5_AUTH_CONTEXT_DO_SEQUENCE); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_auth_con_setflags failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } my_addr = gensec_get_my_addr(gensec_security); if (my_addr && my_addr->sockaddr) { ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context, my_addr->sockaddr, &my_krb5_addr); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } } peer_addr = gensec_get_peer_addr(gensec_security); if (peer_addr && peer_addr->sockaddr) { ret = krb5_sockaddr2address(gensec_krb5_state->smb_krb5_context->krb5_context, peer_addr->sockaddr, &peer_krb5_addr); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_sockaddr2address (local) failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } } ret = krb5_auth_con_setaddrs(gensec_krb5_state->smb_krb5_context->krb5_context, gensec_krb5_state->auth_context, my_addr ? &my_krb5_addr : NULL, peer_addr ? &peer_krb5_addr : NULL); if (ret) { DEBUG(1,("gensec_krb5_start: krb5_auth_con_setaddrs failed (%s)\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); talloc_free(gensec_krb5_state); return NT_STATUS_INTERNAL_ERROR; } return NT_STATUS_OK; }
static NTSTATUS gensec_krb5_common_client_start(struct gensec_security *gensec_security, bool gssapi) { struct gensec_krb5_state *gensec_krb5_state; krb5_error_code ret; NTSTATUS nt_status; struct ccache_container *ccache_container; const char *hostname; const char *principal; krb5_data in_data; hostname = gensec_get_target_hostname(gensec_security); if (!hostname) { DEBUG(1, ("Could not determine hostname for target computer, cannot use kerberos\n")); return NT_STATUS_INVALID_PARAMETER; } if (is_ipaddress(hostname)) { DEBUG(2, ("Cannot do krb5 to an IP address")); return NT_STATUS_INVALID_PARAMETER; } if (strcmp(hostname, "localhost") == 0) { DEBUG(2, ("krb5 to 'localhost' does not make sense")); return NT_STATUS_INVALID_PARAMETER; } nt_status = gensec_krb5_start(gensec_security, gssapi); if (!NT_STATUS_IS_OK(nt_status)) { return nt_status; } gensec_krb5_state = (struct gensec_krb5_state *)gensec_security->private_data; gensec_krb5_state->state_position = GENSEC_KRB5_CLIENT_START; gensec_krb5_state->ap_req_options = AP_OPTS_USE_SUBKEY; if (gensec_krb5_state->gssapi) { /* The Fake GSSAPI modal emulates Samba3, which does not do mutual authentication */ if (gensec_setting_bool(gensec_security->settings, "gensec_fake_gssapi_krb5", "mutual", false)) { gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; } } else { /* The wrapping for KPASSWD (a user of the raw KRB5 API) should be mutually authenticated */ if (gensec_setting_bool(gensec_security->settings, "gensec_krb5", "mutual", true)) { gensec_krb5_state->ap_req_options |= AP_OPTS_MUTUAL_REQUIRED; } } principal = gensec_get_target_principal(gensec_security); ret = cli_credentials_get_ccache(gensec_get_credentials(gensec_security), gensec_security->event_ctx, gensec_security->settings->lp_ctx, &ccache_container); switch (ret) { case 0: break; case KRB5KDC_ERR_PREAUTH_FAILED: return NT_STATUS_LOGON_FAILURE; case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require to contact %s\n", principal)); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ default: DEBUG(1, ("gensec_krb5_start: Aquiring initiator credentials failed: %s\n", error_message(ret))); return NT_STATUS_UNSUCCESSFUL; } in_data.length = 0; if (principal && lp_client_use_spnego_principal(gensec_security->settings->lp_ctx)) { krb5_principal target_principal; ret = krb5_parse_name(gensec_krb5_state->smb_krb5_context->krb5_context, principal, &target_principal); if (ret == 0) { ret = krb5_mk_req_exact(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context, gensec_krb5_state->ap_req_options, target_principal, &in_data, ccache_container->ccache, &gensec_krb5_state->enc_ticket); krb5_free_principal(gensec_krb5_state->smb_krb5_context->krb5_context, target_principal); } } else { ret = krb5_mk_req(gensec_krb5_state->smb_krb5_context->krb5_context, &gensec_krb5_state->auth_context, gensec_krb5_state->ap_req_options, gensec_get_target_service(gensec_security), hostname, &in_data, ccache_container->ccache, &gensec_krb5_state->enc_ticket); } switch (ret) { case 0: return NT_STATUS_OK; case KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: DEBUG(3, ("Server [%s] is not registered with our KDC: %s\n", hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5_KDC_UNREACH: DEBUG(3, ("Cannot reach a KDC we require to contact host [%s]: %s\n", hostname, smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); return NT_STATUS_INVALID_PARAMETER; /* Make SPNEGO ignore us, we can't go any further here */ case KRB5KDC_ERR_PREAUTH_FAILED: case KRB5KRB_AP_ERR_TKT_EXPIRED: case KRB5_CC_END: /* Too much clock skew - we will need to kinit to re-skew the clock */ case KRB5KRB_AP_ERR_SKEW: case KRB5_KDCREP_SKEW: { DEBUG(3, ("kerberos (mk_req) failed: %s\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); /*fall through*/ } /* just don't print a message for these really ordinary messages */ case KRB5_FCC_NOFILE: case KRB5_CC_NOTFOUND: case ENOENT: return NT_STATUS_UNSUCCESSFUL; break; default: DEBUG(0, ("kerberos: %s\n", smb_get_krb5_error_message(gensec_krb5_state->smb_krb5_context->krb5_context, ret, gensec_krb5_state))); return NT_STATUS_UNSUCCESSFUL; } }