Changeset 988 for vendor/current/source3/libads/authdata.c
- Timestamp:
- Nov 24, 2016, 1:14:11 PM (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
vendor/current/source3/libads/authdata.c
r740 r988 27 27 #include "smb_krb5.h" 28 28 #include "libads/kerberos_proto.h" 29 #include "auth/common_auth.h" 30 #include "lib/param/param.h" 31 #include "librpc/crypto/gse.h" 32 #include "auth/gensec/gensec.h" 33 #include "auth/gensec/gensec_internal.h" /* TODO: remove this */ 34 #include "../libcli/auth/spnego.h" 29 35 30 36 #ifdef HAVE_KRB5 31 37 38 #include "auth/kerberos/pac_utils.h" 39 40 struct smb_krb5_context; 41 32 42 /**************************************************************** 43 Callback to get the PAC_LOGON_INFO from the blob 33 44 ****************************************************************/ 34 35 static krb5_error_code check_pac_checksum(TALLOC_CTX *mem_ctx, 36 DATA_BLOB pac_data, 37 struct PAC_SIGNATURE_DATA *sig, 38 krb5_context context, 39 krb5_keyblock *keyblock) 45 static NTSTATUS kerberos_fetch_pac(struct auth4_context *auth_ctx, 46 TALLOC_CTX *mem_ctx, 47 struct smb_krb5_context *smb_krb5_context, 48 DATA_BLOB *pac_blob, 49 const char *princ_name, 50 const struct tsocket_address *remote_address, 51 uint32_t session_info_flags, 52 struct auth_session_info **session_info) 40 53 { 41 krb5_error_code ret; 42 krb5_checksum cksum; 43 krb5_keyusage usage = 0; 44 45 smb_krb5_checksum_from_pac_sig(&cksum, sig); 46 47 #ifdef HAVE_KRB5_KU_OTHER_CKSUM /* Heimdal */ 48 usage = KRB5_KU_OTHER_CKSUM; 49 #elif defined(HAVE_KRB5_KEYUSAGE_APP_DATA_CKSUM) /* MIT */ 50 usage = KRB5_KEYUSAGE_APP_DATA_CKSUM; 51 #else 52 #error UNKNOWN_KRB5_KEYUSAGE 53 #endif 54 55 ret = smb_krb5_verify_checksum(context, 56 keyblock, 57 usage, 58 &cksum, 59 pac_data.data, 60 pac_data.length); 61 62 if (ret) { 63 DEBUG(2,("check_pac_checksum: PAC Verification failed: %s (%d)\n", 64 error_message(ret), ret)); 65 return ret; 66 } 67 68 return ret; 54 TALLOC_CTX *tmp_ctx; 55 struct PAC_DATA *pac_data = NULL; 56 struct PAC_DATA_CTR *pac_data_ctr = NULL; 57 NTSTATUS status = NT_STATUS_INTERNAL_ERROR; 58 59 tmp_ctx = talloc_new(mem_ctx); 60 if (!tmp_ctx) { 61 return NT_STATUS_NO_MEMORY; 62 } 63 64 if (pac_blob != NULL) { 65 status = kerberos_decode_pac(tmp_ctx, 66 *pac_blob, 67 NULL, 68 NULL, 69 NULL, 70 NULL, 71 0, 72 &pac_data); 73 if (!NT_STATUS_IS_OK(status)) { 74 goto done; 75 } 76 77 pac_data_ctr = talloc(mem_ctx, struct PAC_DATA_CTR); 78 if (pac_data_ctr == NULL) { 79 status = NT_STATUS_NO_MEMORY; 80 goto done; 81 } 82 83 talloc_set_name_const(pac_data_ctr, "struct PAC_DATA_CTR"); 84 85 pac_data_ctr->pac_data = talloc_steal(pac_data_ctr, pac_data); 86 pac_data_ctr->pac_blob = data_blob_talloc(pac_data_ctr, 87 pac_blob->data, 88 pac_blob->length); 89 90 auth_ctx->private_data = talloc_steal(auth_ctx, pac_data_ctr); 91 } 92 93 *session_info = talloc_zero(mem_ctx, struct auth_session_info); 94 if (!*session_info) { 95 status = NT_STATUS_NO_MEMORY; 96 goto done; 97 } 98 status = NT_STATUS_OK; 99 100 done: 101 TALLOC_FREE(tmp_ctx); 102 103 return status; 69 104 } 70 105 71 /** 72 * @brief Decode a blob containing a NDR envoded PAC structure 73 * 74 * @param mem_ctx - The memory context 75 * @param pac_data_blob - The data blob containing the NDR encoded data 76 * @param context - The Kerberos Context 77 * @param service_keyblock - The Service Key used to verify the checksum 78 * @param client_principal - The client principal 79 * @param tgs_authtime - The ticket timestamp 80 * @param pac_data_out - [out] The decoded PAC 81 * 82 * @return - A NTSTATUS error code 83 */ 84 NTSTATUS decode_pac_data(TALLOC_CTX *mem_ctx, 85 DATA_BLOB *pac_data_blob, 86 krb5_context context, 87 krb5_keyblock *service_keyblock, 88 krb5_const_principal client_principal, 89 time_t tgs_authtime, 90 struct PAC_DATA **pac_data_out) 91 { 92 NTSTATUS status; 93 enum ndr_err_code ndr_err; 94 krb5_error_code ret; 95 DATA_BLOB modified_pac_blob; 96 97 NTTIME tgs_authtime_nttime; 98 krb5_principal client_principal_pac = NULL; 99 int i; 100 101 struct PAC_SIGNATURE_DATA *srv_sig_ptr = NULL; 102 struct PAC_SIGNATURE_DATA *kdc_sig_ptr = NULL; 103 struct PAC_SIGNATURE_DATA *srv_sig_wipe = NULL; 104 struct PAC_SIGNATURE_DATA *kdc_sig_wipe = NULL; 105 struct PAC_LOGON_NAME *logon_name = NULL; 106 struct PAC_LOGON_INFO *logon_info = NULL; 107 struct PAC_DATA *pac_data = NULL; 108 struct PAC_DATA_RAW *pac_data_raw = NULL; 109 110 DATA_BLOB *srv_sig_blob = NULL; 111 DATA_BLOB *kdc_sig_blob = NULL; 112 113 bool bool_ret; 114 115 *pac_data_out = NULL; 116 117 pac_data = TALLOC_ZERO_P(mem_ctx, struct PAC_DATA); 118 pac_data_raw = TALLOC_ZERO_P(mem_ctx, struct PAC_DATA_RAW); 119 kdc_sig_wipe = TALLOC_ZERO_P(mem_ctx, struct PAC_SIGNATURE_DATA); 120 srv_sig_wipe = TALLOC_ZERO_P(mem_ctx, struct PAC_SIGNATURE_DATA); 121 if (!pac_data_raw || !pac_data || !kdc_sig_wipe || !srv_sig_wipe) { 122 return NT_STATUS_NO_MEMORY; 123 } 124 125 ndr_err = ndr_pull_struct_blob(pac_data_blob, pac_data, pac_data, 126 (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA); 127 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 128 status = ndr_map_error2ntstatus(ndr_err); 129 DEBUG(0,("can't parse the PAC: %s\n", 130 nt_errstr(status))); 131 return status; 132 } 133 134 if (pac_data->num_buffers < 4) { 135 /* we need logon_ingo, service_key and kdc_key */ 136 DEBUG(0,("less than 4 PAC buffers\n")); 137 return NT_STATUS_INVALID_PARAMETER; 138 } 139 140 ndr_err = ndr_pull_struct_blob( 141 pac_data_blob, pac_data_raw, pac_data_raw, 142 (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA_RAW); 143 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 144 status = ndr_map_error2ntstatus(ndr_err); 145 DEBUG(0,("can't parse the PAC: %s\n", 146 nt_errstr(status))); 147 return status; 148 } 149 150 if (pac_data_raw->num_buffers < 4) { 151 /* we need logon_ingo, service_key and kdc_key */ 152 DEBUG(0,("less than 4 PAC buffers\n")); 153 return NT_STATUS_INVALID_PARAMETER; 154 } 155 156 if (pac_data->num_buffers != pac_data_raw->num_buffers) { 157 /* we need logon_ingo, service_key and kdc_key */ 158 DEBUG(0, ("misparse! PAC_DATA has %d buffers while " 159 "PAC_DATA_RAW has %d\n", pac_data->num_buffers, 160 pac_data_raw->num_buffers)); 161 return NT_STATUS_INVALID_PARAMETER; 162 } 163 164 for (i=0; i < pac_data->num_buffers; i++) { 165 struct PAC_BUFFER *data_buf = &pac_data->buffers[i]; 166 struct PAC_BUFFER_RAW *raw_buf = &pac_data_raw->buffers[i]; 167 168 if (data_buf->type != raw_buf->type) { 169 DEBUG(0, ("misparse! PAC_DATA buffer %d has type " 170 "%d while PAC_DATA_RAW has %d\n", i, 171 data_buf->type, raw_buf->type)); 172 return NT_STATUS_INVALID_PARAMETER; 173 } 174 switch (data_buf->type) { 175 case PAC_TYPE_LOGON_INFO: 176 if (!data_buf->info) { 177 break; 178 } 179 logon_info = data_buf->info->logon_info.info; 180 break; 181 case PAC_TYPE_SRV_CHECKSUM: 182 if (!data_buf->info) { 183 break; 184 } 185 srv_sig_ptr = &data_buf->info->srv_cksum; 186 srv_sig_blob = &raw_buf->info->remaining; 187 break; 188 case PAC_TYPE_KDC_CHECKSUM: 189 if (!data_buf->info) { 190 break; 191 } 192 kdc_sig_ptr = &data_buf->info->kdc_cksum; 193 kdc_sig_blob = &raw_buf->info->remaining; 194 break; 195 case PAC_TYPE_LOGON_NAME: 196 logon_name = &data_buf->info->logon_name; 197 break; 198 default: 199 break; 200 } 201 } 202 203 if (!logon_info) { 204 DEBUG(0,("PAC no logon_info\n")); 205 return NT_STATUS_INVALID_PARAMETER; 206 } 207 208 if (!logon_name) { 209 DEBUG(0,("PAC no logon_name\n")); 210 return NT_STATUS_INVALID_PARAMETER; 211 } 212 213 if (!srv_sig_ptr || !srv_sig_blob) { 214 DEBUG(0,("PAC no srv_key\n")); 215 return NT_STATUS_INVALID_PARAMETER; 216 } 217 218 if (!kdc_sig_ptr || !kdc_sig_blob) { 219 DEBUG(0,("PAC no kdc_key\n")); 220 return NT_STATUS_INVALID_PARAMETER; 221 } 222 223 /* Find and zero out the signatures, 224 * as required by the signing algorithm */ 225 226 /* We find the data blobs above, 227 * now we parse them to get at the exact portion we should zero */ 228 ndr_err = ndr_pull_struct_blob( 229 kdc_sig_blob, kdc_sig_wipe, kdc_sig_wipe, 230 (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA); 231 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 232 status = ndr_map_error2ntstatus(ndr_err); 233 DEBUG(0,("can't parse the KDC signature: %s\n", 234 nt_errstr(status))); 235 return status; 236 } 237 238 ndr_err = ndr_pull_struct_blob( 239 srv_sig_blob, srv_sig_wipe, srv_sig_wipe, 240 (ndr_pull_flags_fn_t)ndr_pull_PAC_SIGNATURE_DATA); 241 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 242 status = ndr_map_error2ntstatus(ndr_err); 243 DEBUG(0,("can't parse the SRV signature: %s\n", 244 nt_errstr(status))); 245 return status; 246 } 247 248 /* Now zero the decoded structure */ 249 memset(kdc_sig_wipe->signature.data, 250 '\0', kdc_sig_wipe->signature.length); 251 memset(srv_sig_wipe->signature.data, 252 '\0', srv_sig_wipe->signature.length); 253 254 /* and reencode, back into the same place it came from */ 255 ndr_err = ndr_push_struct_blob( 256 kdc_sig_blob, pac_data_raw, kdc_sig_wipe, 257 (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA); 258 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 259 status = ndr_map_error2ntstatus(ndr_err); 260 DEBUG(0,("can't repack the KDC signature: %s\n", 261 nt_errstr(status))); 262 return status; 263 } 264 ndr_err = ndr_push_struct_blob( 265 srv_sig_blob, pac_data_raw, srv_sig_wipe, 266 (ndr_push_flags_fn_t)ndr_push_PAC_SIGNATURE_DATA); 267 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 268 status = ndr_map_error2ntstatus(ndr_err); 269 DEBUG(0,("can't repack the SRV signature: %s\n", 270 nt_errstr(status))); 271 return status; 272 } 273 274 /* push out the whole structure, but now with zero'ed signatures */ 275 ndr_err = ndr_push_struct_blob( 276 &modified_pac_blob, pac_data_raw, pac_data_raw, 277 (ndr_push_flags_fn_t)ndr_push_PAC_DATA_RAW); 278 if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) { 279 status = ndr_map_error2ntstatus(ndr_err); 280 DEBUG(0,("can't repack the RAW PAC: %s\n", 281 nt_errstr(status))); 282 return status; 283 } 284 285 /* verify by service_key */ 286 ret = check_pac_checksum(mem_ctx, 287 modified_pac_blob, srv_sig_ptr, 288 context, 289 service_keyblock); 290 if (ret) { 291 DEBUG(1, ("PAC Decode: Failed to verify the service " 292 "signature: %s\n", error_message(ret))); 293 return NT_STATUS_ACCESS_DENIED; 294 } 295 296 /* Convert to NT time, so as not to loose accuracy in comparison */ 297 unix_to_nt_time(&tgs_authtime_nttime, tgs_authtime); 298 299 if (tgs_authtime_nttime != logon_name->logon_time) { 300 DEBUG(2, ("PAC Decode: " 301 "Logon time mismatch between ticket and PAC!\n")); 302 DEBUG(2, ("PAC Decode: PAC: %s\n", 303 nt_time_string(mem_ctx, logon_name->logon_time))); 304 DEBUG(2, ("PAC Decode: Ticket: %s\n", 305 nt_time_string(mem_ctx, tgs_authtime_nttime))); 306 return NT_STATUS_ACCESS_DENIED; 307 } 308 309 ret = smb_krb5_parse_name_norealm(context, 310 logon_name->account_name, 311 &client_principal_pac); 312 if (ret) { 313 DEBUG(2, ("Could not parse name from PAC: [%s]:%s\n", 314 logon_name->account_name, error_message(ret))); 315 return NT_STATUS_INVALID_PARAMETER; 316 } 317 318 bool_ret = smb_krb5_principal_compare_any_realm(context, 319 client_principal, 320 client_principal_pac); 321 322 krb5_free_principal(context, client_principal_pac); 323 324 if (!bool_ret) { 325 DEBUG(2, ("Name in PAC [%s] does not match principal name " 326 "in ticket\n", logon_name->account_name)); 327 return NT_STATUS_ACCESS_DENIED; 328 } 329 330 DEBUG(3,("Found account name from PAC: %s [%s]\n", 331 logon_info->info3.base.account_name.string, 332 logon_info->info3.base.full_name.string)); 333 334 DEBUG(10,("Successfully validated Kerberos PAC\n")); 335 336 if (DEBUGLEVEL >= 10) { 337 const char *s; 338 s = NDR_PRINT_STRUCT_STRING(mem_ctx, PAC_DATA, pac_data); 339 if (s) { 340 DEBUGADD(10,("%s\n", s)); 341 } 342 } 343 344 *pac_data_out = pac_data; 345 346 return NT_STATUS_OK; 347 } 348 349 /**************************************************************** 350 Given a username, password and other details, return the 351 PAC_LOGON_INFO (the structure containing the important user 352 information such as groups). 353 ****************************************************************/ 354 106 /* 107 * Given the username/password, do a kinit, store the ticket in 108 * cache_name if specified, and return the PAC_LOGON_INFO (the 109 * structure containing the important user information such as 110 * groups). 111 */ 355 112 NTSTATUS kerberos_return_pac(TALLOC_CTX *mem_ctx, 356 113 const char *name, … … 364 121 time_t renewable_time, 365 122 const char *impersonate_princ_s, 366 struct PAC_LOGON_INFO **logon_info) 123 const char *local_service, 124 struct PAC_DATA_CTR **_pac_data_ctr) 367 125 { 368 126 krb5_error_code ret; 369 127 NTSTATUS status = NT_STATUS_INVALID_PARAMETER; 370 DATA_BLOB tkt, ap_rep, sesskey1, sesskey2; 371 char *client_princ_out = NULL; 128 DATA_BLOB tkt, tkt_wrapped, ap_rep, sesskey1; 372 129 const char *auth_princ = NULL; 373 const char *local_service = NULL;374 130 const char *cc = "MEMORY:kerberos_return_pac"; 131 struct auth_session_info *session_info; 132 struct gensec_security *gensec_server_context; 133 const struct gensec_security_ops **backends; 134 struct gensec_settings *gensec_settings; 135 size_t idx = 0; 136 struct auth4_context *auth_context; 137 struct loadparm_context *lp_ctx; 138 struct PAC_DATA_CTR *pac_data_ctr = NULL; 139 140 TALLOC_CTX *tmp_ctx = talloc_new(mem_ctx); 141 NT_STATUS_HAVE_NO_MEMORY(tmp_ctx); 375 142 376 143 ZERO_STRUCT(tkt); 377 144 ZERO_STRUCT(ap_rep); 378 145 ZERO_STRUCT(sesskey1); 379 ZERO_STRUCT(sesskey2);380 146 381 147 if (!name || !pass) { … … 394 160 } 395 161 NT_STATUS_HAVE_NO_MEMORY(auth_princ); 396 397 local_service = talloc_asprintf(mem_ctx, "%s$@%s",398 global_myname(), lp_realm());399 NT_STATUS_HAVE_NO_MEMORY(local_service);400 162 401 163 ret = kerberos_kinit_password_ext(auth_princ, … … 455 217 goto out; 456 218 } 457 status = ads_verify_ticket(mem_ctx, 458 lp_realm(), 459 time_offset, 460 &tkt, 461 &client_princ_out, 462 logon_info, 463 &ap_rep, 464 &sesskey2, 465 False); 219 220 /* wrap that up in a nice GSS-API wrapping */ 221 tkt_wrapped = spnego_gen_krb5_wrap(tmp_ctx, tkt, TOK_ID_KRB_AP_REQ); 222 if (tkt_wrapped.data == NULL) { 223 status = NT_STATUS_NO_MEMORY; 224 goto out; 225 } 226 227 auth_context = talloc_zero(tmp_ctx, struct auth4_context); 228 if (auth_context == NULL) { 229 status = NT_STATUS_NO_MEMORY; 230 goto out; 231 } 232 auth_context->generate_session_info_pac = kerberos_fetch_pac; 233 234 lp_ctx = loadparm_init_s3(tmp_ctx, loadparm_s3_helpers()); 235 if (lp_ctx == NULL) { 236 status = NT_STATUS_INVALID_SERVER_STATE; 237 DEBUG(10, ("loadparm_init_s3 failed\n")); 238 goto out; 239 } 240 241 gensec_settings = lpcfg_gensec_settings(tmp_ctx, lp_ctx); 242 if (gensec_settings == NULL) { 243 status = NT_STATUS_NO_MEMORY; 244 DEBUG(10, ("lpcfg_gensec_settings failed\n")); 245 goto out; 246 } 247 248 backends = talloc_zero_array(gensec_settings, 249 const struct gensec_security_ops *, 2); 250 if (backends == NULL) { 251 status = NT_STATUS_NO_MEMORY; 252 goto out; 253 } 254 gensec_settings->backends = backends; 255 256 gensec_init(); 257 258 backends[idx++] = &gensec_gse_krb5_security_ops; 259 260 status = gensec_server_start(tmp_ctx, gensec_settings, 261 auth_context, &gensec_server_context); 262 466 263 if (!NT_STATUS_IS_OK(status)) { 467 DEBUG(1,("ads_verify_ticket failed: %s\n", 468 nt_errstr(status))); 469 goto out; 470 } 471 472 if (!*logon_info) { 264 DEBUG(1, (__location__ "Failed to start server-side GENSEC to validate a Kerberos ticket: %s\n", nt_errstr(status))); 265 goto out; 266 } 267 268 talloc_unlink(tmp_ctx, lp_ctx); 269 talloc_unlink(tmp_ctx, gensec_settings); 270 talloc_unlink(tmp_ctx, auth_context); 271 272 status = gensec_start_mech_by_oid(gensec_server_context, GENSEC_OID_KERBEROS5); 273 if (!NT_STATUS_IS_OK(status)) { 274 DEBUG(1, (__location__ "Failed to start server-side GENSEC krb5 to validate a Kerberos ticket: %s\n", nt_errstr(status))); 275 goto out; 276 } 277 278 /* Do a client-server update dance */ 279 status = gensec_update(gensec_server_context, tmp_ctx, tkt_wrapped, &ap_rep); 280 if (!NT_STATUS_IS_OK(status)) { 281 DEBUG(1, ("gensec_update() failed: %s\n", nt_errstr(status))); 282 goto out; 283 } 284 285 /* Now return the PAC information to the callers. We ingore 286 * the session_info and instead pick out the PAC via the 287 * private_data on the auth_context */ 288 status = gensec_session_info(gensec_server_context, tmp_ctx, &session_info); 289 if (!NT_STATUS_IS_OK(status)) { 290 DEBUG(1, ("Unable to obtain PAC via gensec_session_info\n")); 291 goto out; 292 } 293 294 pac_data_ctr = talloc_get_type_abort(gensec_server_context->auth_context->private_data, 295 struct PAC_DATA_CTR); 296 if (pac_data_ctr == NULL) { 473 297 DEBUG(1,("no PAC\n")); 474 298 status = NT_STATUS_INVALID_PARAMETER; … … 476 300 } 477 301 302 *_pac_data_ctr = talloc_move(mem_ctx, &pac_data_ctr); 303 478 304 out: 305 talloc_free(tmp_ctx); 479 306 if (cc != cache_name) { 480 307 ads_kdestroy(cc); … … 484 311 data_blob_free(&ap_rep); 485 312 data_blob_free(&sesskey1); 486 data_blob_free(&sesskey2);487 488 TALLOC_FREE(client_princ_out);489 313 490 314 return status;
Note:
See TracChangeset
for help on using the changeset viewer.