static int test_libntlm_v1(int flags) { const char *user = "******", *domain = "mydomain", *password = "******"; OM_uint32 maj_stat, min_stat; gss_ctx_id_t ctx = GSS_C_NO_CONTEXT; gss_buffer_desc input, output; struct ntlm_type1 type1; struct ntlm_type2 type2; struct ntlm_type3 type3; struct ntlm_buf data; krb5_error_code ret; gss_name_t src_name = GSS_C_NO_NAME; memset(&type1, 0, sizeof(type1)); memset(&type2, 0, sizeof(type2)); memset(&type3, 0, sizeof(type3)); type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_TARGET|NTLM_NEG_NTLM|flags; type1.domain = strdup(domain); type1.hostname = NULL; type1.os[0] = 0; type1.os[1] = 0; ret = heim_ntlm_encode_type1(&type1, &data); if (ret) errx(1, "heim_ntlm_encode_type1"); input.value = data.data; input.length = data.length; output.length = 0; output.value = NULL; maj_stat = gss_accept_sec_context(&min_stat, &ctx, GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, NULL, NULL, &output, NULL, NULL, NULL); free(data.data); if (GSS_ERROR(maj_stat)) errx(1, "accept_sec_context v1: %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); if (output.length == 0) errx(1, "output.length == 0"); data.data = output.value; data.length = output.length; ret = heim_ntlm_decode_type2(&data, &type2); if (ret) errx(1, "heim_ntlm_decode_type2"); gss_release_buffer(&min_stat, &output); type3.flags = type2.flags; type3.username = rk_UNCONST(user); type3.targetname = type2.targetname; type3.ws = rk_UNCONST("workstation"); { struct ntlm_buf key; heim_ntlm_nt_key(password, &key); heim_ntlm_calculate_ntlm1(key.data, key.length, type2.challenge, &type3.ntlm); if (flags & NTLM_NEG_KEYEX) { struct ntlm_buf sessionkey; heim_ntlm_build_ntlm1_master(key.data, key.length, &sessionkey, &type3.sessionkey); free(sessionkey.data); } free(key.data); } ret = heim_ntlm_encode_type3(&type3, &data); if (ret) errx(1, "heim_ntlm_encode_type3"); input.length = data.length; input.value = data.data; maj_stat = gss_accept_sec_context(&min_stat, &ctx, GSS_C_NO_CREDENTIAL, &input, GSS_C_NO_CHANNEL_BINDINGS, &src_name, NULL, &output, NULL, NULL, NULL); free(input.value); if (maj_stat != GSS_S_COMPLETE) errx(1, "accept_sec_context v1 2 %s", gssapi_err(maj_stat, min_stat, GSS_C_NO_OID)); gss_release_buffer(&min_stat, &output); gss_delete_sec_context(&min_stat, &ctx, NULL); if (src_name == GSS_C_NO_NAME) errx(1, "no source name!"); gss_display_name(&min_stat, src_name, &output, NULL); printf("src_name: %.*s\n", (int)output.length, (char*)output.value); gss_release_name(&min_stat, &src_name); gss_release_buffer(&min_stat, &output); return 0; }
OM_uint32 GSSAPI_CALLCONV _gss_ntlm_init_sec_context (OM_uint32 * minor_status, gss_const_cred_id_t initiator_cred_handle, gss_ctx_id_t * context_handle, gss_const_name_t target_name, const gss_OID mech_type, OM_uint32 req_flags, OM_uint32 time_req, const gss_channel_bindings_t input_chan_bindings, const gss_buffer_t input_token, gss_OID * actual_mech_type, gss_buffer_t output_token, OM_uint32 * ret_flags, OM_uint32 * time_rec ) { ntlm_ctx ctx; ntlm_name name = (ntlm_name)target_name; *minor_status = 0; if (ret_flags) *ret_flags = 0; if (time_rec) *time_rec = 0; if (actual_mech_type) *actual_mech_type = GSS_C_NO_OID; if (*context_handle == GSS_C_NO_CONTEXT) { struct ntlm_type1 type1; struct ntlm_buf data; uint32_t flags = 0; int ret; ctx = calloc(1, sizeof(*ctx)); if (ctx == NULL) { *minor_status = EINVAL; return GSS_S_FAILURE; } *context_handle = (gss_ctx_id_t)ctx; if (initiator_cred_handle != GSS_C_NO_CREDENTIAL) { ntlm_cred cred = (ntlm_cred)initiator_cred_handle; ret = _gss_copy_cred(cred, &ctx->client); } else ret = _gss_ntlm_get_user_cred(name, &ctx->client); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } if (req_flags & GSS_C_CONF_FLAG) flags |= NTLM_NEG_SEAL; if (req_flags & GSS_C_INTEG_FLAG) flags |= NTLM_NEG_SIGN; else flags |= NTLM_NEG_ALWAYS_SIGN; flags |= NTLM_NEG_UNICODE; flags |= NTLM_NEG_NTLM; flags |= NTLM_NEG_NTLM2_SESSION; flags |= NTLM_NEG_KEYEX; memset(&type1, 0, sizeof(type1)); type1.flags = flags; type1.domain = name->domain; type1.hostname = NULL; type1.os[0] = 0; type1.os[1] = 0; ret = heim_ntlm_encode_type1(&type1, &data); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } output_token->value = data.data; output_token->length = data.length; return GSS_S_CONTINUE_NEEDED; } else { krb5_error_code ret; struct ntlm_type2 type2; struct ntlm_type3 type3; struct ntlm_buf data; ctx = (ntlm_ctx)*context_handle; data.data = input_token->value; data.length = input_token->length; ret = heim_ntlm_decode_type2(&data, &type2); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } ctx->flags = type2.flags; /* XXX check that type2.targetinfo matches `target_name´ */ /* XXX check verify targetinfo buffer */ memset(&type3, 0, sizeof(type3)); type3.username = ctx->client->username; type3.flags = type2.flags; type3.targetname = type2.targetname; type3.ws = rk_UNCONST("workstation"); /* * NTLM Version 1 if no targetinfo buffer. */ if (1 || type2.targetinfo.length == 0) { struct ntlm_buf sessionkey; if (type2.flags & NTLM_NEG_NTLM2_SESSION) { unsigned char nonce[8]; if (RAND_bytes(nonce, sizeof(nonce)) != 1) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = EINVAL; return GSS_S_FAILURE; } ret = heim_ntlm_calculate_ntlm2_sess(nonce, type2.challenge, ctx->client->key.data, &type3.lm, &type3.ntlm); } else { ret = heim_ntlm_calculate_ntlm1(ctx->client->key.data, ctx->client->key.length, type2.challenge, &type3.ntlm); } if (ret) { _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); *minor_status = ret; return GSS_S_FAILURE; } ret = heim_ntlm_build_ntlm1_master(ctx->client->key.data, ctx->client->key.length, &sessionkey, &type3.sessionkey); if (ret) { if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); *minor_status = ret; return GSS_S_FAILURE; } ret = krb5_data_copy(&ctx->sessionkey, sessionkey.data, sessionkey.length); free(sessionkey.data); if (ret) { if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); _gss_ntlm_delete_sec_context(minor_status,context_handle,NULL); *minor_status = ret; return GSS_S_FAILURE; } ctx->status |= STATUS_SESSIONKEY; } else { struct ntlm_buf sessionkey; unsigned char ntlmv2[16]; struct ntlm_targetinfo ti; /* verify infotarget */ ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); if(ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = EINVAL; return GSS_S_FAILURE; } ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, ctx->client->key.length, ctx->client->username, name->domain, type2.challenge, &type2.targetinfo, ntlmv2, &type3.ntlm); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), &sessionkey, &type3.sessionkey); memset(ntlmv2, 0, sizeof(ntlmv2)); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } ctx->flags |= NTLM_NEG_NTLM2_SESSION; ret = krb5_data_copy(&ctx->sessionkey, sessionkey.data, sessionkey.length); free(sessionkey.data); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } } if (ctx->flags & NTLM_NEG_NTLM2_SESSION) { ctx->status |= STATUS_SESSIONKEY; _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), ctx->sessionkey.data, ctx->sessionkey.length); _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), ctx->sessionkey.data, ctx->sessionkey.length); } else { ctx->status |= STATUS_SESSIONKEY; RC4_set_key(&ctx->u.v1.crypto_recv.key, ctx->sessionkey.length, ctx->sessionkey.data); RC4_set_key(&ctx->u.v1.crypto_send.key, ctx->sessionkey.length, ctx->sessionkey.data); } ret = heim_ntlm_encode_type3(&type3, &data, NULL); free(type3.sessionkey.data); if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } output_token->length = data.length; output_token->value = data.data; if (actual_mech_type) *actual_mech_type = GSS_NTLM_MECHANISM; if (ret_flags) *ret_flags = 0; if (time_rec) *time_rec = GSS_C_INDEFINITE; ctx->status |= STATUS_OPEN; return GSS_S_COMPLETE; } }
static krb5_error_code kcm_op_do_ntlm(krb5_context context, kcm_client *client, kcm_operation opcode, krb5_storage *request, krb5_storage *response) { struct kcm_ntlm_cred *c; struct ntlm_type2 type2; struct ntlm_type3 type3; char *user = NULL, *domain = NULL; struct ntlm_buf ndata, sessionkey; krb5_data data; krb5_error_code ret; uint32_t flags = 0; memset(&type2, 0, sizeof(type2)); memset(&type3, 0, sizeof(type3)); sessionkey.data = NULL; sessionkey.length = 0; ret = krb5_ret_stringz(request, &user); if (ret) goto error; ret = krb5_ret_stringz(request, &domain); if (ret) goto error; if (domain[0] == '\0') { free(domain); domain = NULL; } c = find_ntlm_cred(user, domain, client); if (c == NULL) { ret = EINVAL; goto error; } ret = krb5_ret_data(request, &data); if (ret) goto error; ndata.data = data.data; ndata.length = data.length; ret = heim_ntlm_decode_type2(&ndata, &type2); krb5_data_free(&data); if (ret) goto error; if (domain && strcmp(domain, type2.targetname) == 0) { ret = EINVAL; goto error; } type3.username = c->user; type3.flags = type2.flags; type3.targetname = type2.targetname; type3.ws = rk_UNCONST("workstation"); /* * NTLM Version 1 if no targetinfo buffer. */ if (1 || type2.targetinfo.length == 0) { struct ntlm_buf sessionkey; if (type2.flags & NTLM_NEG_NTLM2_SESSION) { unsigned char nonce[8]; if (RAND_bytes(nonce, sizeof(nonce)) != 1) { ret = EINVAL; goto error; } ret = heim_ntlm_calculate_ntlm2_sess(nonce, type2.challenge, c->nthash.data, &type3.lm, &type3.ntlm); } else { ret = heim_ntlm_calculate_ntlm1(c->nthash.data, c->nthash.length, type2.challenge, &type3.ntlm); } if (ret) goto error; ret = heim_ntlm_build_ntlm1_master(c->nthash.data, c->nthash.length, &sessionkey, &type3.sessionkey); if (ret) { if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); goto error; } free(sessionkey.data); if (ret) { if (type3.lm.data) free(type3.lm.data); if (type3.ntlm.data) free(type3.ntlm.data); goto error; } flags |= NTLM_FLAG_SESSIONKEY; #if 0 } else { struct ntlm_buf sessionkey; unsigned char ntlmv2[16]; struct ntlm_targetinfo ti; /* verify infotarget */ ret = heim_ntlm_decode_targetinfo(&type2.targetinfo, 1, &ti); if(ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } if (ti.domainname && strcmp(ti.domainname, name->domain) != 0) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = EINVAL; return GSS_S_FAILURE; } ret = heim_ntlm_calculate_ntlm2(ctx->client->key.data, ctx->client->key.length, type3.username, name->domain, type2.challenge, &type2.targetinfo, ntlmv2, &type3.ntlm); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } ret = heim_ntlm_build_ntlm1_master(ntlmv2, sizeof(ntlmv2), &sessionkey, &type3.sessionkey); memset(ntlmv2, 0, sizeof(ntlmv2)); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } flags |= NTLM_FLAG_NTLM2_SESSION | NTLM_FLAG_SESSION; if (type3.flags & NTLM_NEG_KEYEX) flags |= NTLM_FLAG_KEYEX; ret = krb5_data_copy(&ctx->sessionkey, sessionkey.data, sessionkey.length); free(sessionkey.data); if (ret) { _gss_ntlm_delete_sec_context(minor_status, context_handle, NULL); *minor_status = ret; return GSS_S_FAILURE; } #endif } #if 0 if (flags & NTLM_FLAG_NTLM2_SESSION) { _gss_ntlm_set_key(&ctx->u.v2.send, 0, (ctx->flags & NTLM_NEG_KEYEX), ctx->sessionkey.data, ctx->sessionkey.length); _gss_ntlm_set_key(&ctx->u.v2.recv, 1, (ctx->flags & NTLM_NEG_KEYEX), ctx->sessionkey.data, ctx->sessionkey.length); } else { flags |= NTLM_FLAG_SESSION; RC4_set_key(&ctx->u.v1.crypto_recv.key, ctx->sessionkey.length, ctx->sessionkey.data); RC4_set_key(&ctx->u.v1.crypto_send.key, ctx->sessionkey.length, ctx->sessionkey.data); } #endif ret = heim_ntlm_encode_type3(&type3, &ndata); if (ret) goto error; data.data = ndata.data; data.length = ndata.length; ret = krb5_store_data(response, data); heim_ntlm_free_buf(&ndata); if (ret) goto error; ret = krb5_store_int32(response, flags); if (ret) goto error; data.data = sessionkey.data; data.length = sessionkey.length; ret = krb5_store_data(response, data); if (ret) goto error; error: free(type3.username); heim_ntlm_free_type2(&type2); free(user); if (domain) free(domain); return ret; }
static void ntlm_service(void *ctx, const heim_idata *req, const heim_icred cred, heim_ipc_complete complete, heim_sipc_call cctx) { NTLMRequest2 ntq; unsigned char sessionkey[16]; heim_idata rep = { 0, NULL }; krb5_context context = ctx; hdb_entry_ex *user = NULL; Key *key = NULL; NTLMReply ntp; size_t size; int ret; char *domain; kdc_log(context, config, 1, "digest-request: uid=%d", (int)heim_ipc_cred_get_uid(cred)); if (heim_ipc_cred_get_uid(cred) != 0) { (*complete)(cctx, EPERM, NULL); return; } ntp.success = 0; ntp.flags = 0; ntp.sessionkey = NULL; ret = decode_NTLMRequest2(req->data, req->length, &ntq, NULL); if (ret) goto failed; /* XXX forward to NetrLogonSamLogonEx() if not a local domain */ if (strcmp(ntq.loginDomainName, "BUILTIN") == 0) { domain = ntq.loginDomainName; } else if (strcmp(ntq.loginDomainName, "") == 0) { domain = "BUILTIN"; } else { ret = EINVAL; goto failed; } kdc_log(context, config, 1, "digest-request: user=%s/%s", ntq.loginUserName, domain); if (ntq.lmchallenge.length != 8) goto failed; if (ntq.ntChallengeResponce.length == 0) goto failed; { krb5_principal client; ret = krb5_make_principal(context, &client, domain, ntq.loginUserName, NULL); if (ret) goto failed; krb5_principal_set_type(context, client, KRB5_NT_NTLM); ret = _kdc_db_fetch(context, config, client, HDB_F_GET_CLIENT, NULL, NULL, &user); krb5_free_principal(context, client); if (ret) goto failed; ret = hdb_enctype2key(context, &user->entry, ETYPE_ARCFOUR_HMAC_MD5, &key); if (ret) { krb5_set_error_message(context, ret, "NTLM missing arcfour key"); goto failed; } } kdc_log(context, config, 2, "digest-request: found user, processing ntlm request", ret); if (ntq.ntChallengeResponce.length != 24) { struct ntlm_buf infotarget, answer; answer.length = ntq.ntChallengeResponce.length; answer.data = ntq.ntChallengeResponce.data; ret = heim_ntlm_verify_ntlm2(key->key.keyvalue.data, key->key.keyvalue.length, ntq.loginUserName, ntq.loginDomainName, 0, ntq.lmchallenge.data, &answer, &infotarget, sessionkey); if (ret) { goto failed; } free(infotarget.data); /* XXX verify info target */ } else { struct ntlm_buf answer; if (ntq.flags & NTLM_NEG_NTLM2_SESSION) { unsigned char sessionhash[MD5_DIGEST_LENGTH]; EVP_MD_CTX *md5ctx; /* the first first 8 bytes is the challenge, what is the other 16 bytes ? */ if (ntq.lmChallengeResponce.length != 24) goto failed; md5ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(md5ctx, EVP_md5(), NULL); EVP_DigestUpdate(md5ctx, ntq.lmchallenge.data, 8); EVP_DigestUpdate(md5ctx, ntq.lmChallengeResponce.data, 8); EVP_DigestFinal_ex(md5ctx, sessionhash, NULL); EVP_MD_CTX_destroy(md5ctx); memcpy(ntq.lmchallenge.data, sessionhash, ntq.lmchallenge.length); } ret = heim_ntlm_calculate_ntlm1(key->key.keyvalue.data, key->key.keyvalue.length, ntq.lmchallenge.data, &answer); if (ret) goto failed; if (ntq.ntChallengeResponce.length != answer.length || memcmp(ntq.ntChallengeResponce.data, answer.data, answer.length) != 0) { free(answer.data); ret = EINVAL; goto failed; } free(answer.data); { EVP_MD_CTX *ctx; ctx = EVP_MD_CTX_create(); EVP_DigestInit_ex(ctx, EVP_md4(), NULL); EVP_DigestUpdate(ctx, key->key.keyvalue.data, key->key.keyvalue.length); EVP_DigestFinal_ex(ctx, sessionkey, NULL); EVP_MD_CTX_destroy(ctx); } } ntp.success = 1; ASN1_MALLOC_ENCODE(NTLMReply, rep.data, rep.length, &ntp, &size, ret); if (ret) goto failed; if (rep.length != size) abort(); failed: kdc_log(context, config, 1, "digest-request: %d", ret); (*complete)(cctx, ret, &rep); free(rep.data); free_NTLMRequest2(&ntq); if (user) _kdc_free_ent (context, user); }
static int test_parse(void) { const char *user = "******", *domain = "mydomain", *password = "******", *target = "DOMAIN"; struct ntlm_type1 type1; struct ntlm_type2 type2; struct ntlm_type3 type3; struct ntlm_buf data; int ret, flags; memset(&type1, 0, sizeof(type1)); type1.flags = NTLM_NEG_UNICODE|NTLM_NEG_TARGET|NTLM_NEG_NTLM; type1.domain = rk_UNCONST(domain); type1.hostname = NULL; type1.os[0] = 0; type1.os[1] = 0; ret = heim_ntlm_encode_type1(&type1, &data); if (ret) errx(1, "heim_ntlm_encode_type1"); memset(&type1, 0, sizeof(type1)); ret = heim_ntlm_decode_type1(&data, &type1); free(data.data); if (ret) errx(1, "heim_ntlm_encode_type1"); heim_ntlm_free_type1(&type1); /* * */ memset(&type2, 0, sizeof(type2)); flags = NTLM_NEG_UNICODE | NTLM_NEG_NTLM | NTLM_TARGET_DOMAIN; type2.flags = flags; memset(type2.challenge, 0x7f, sizeof(type2.challenge)); type2.targetname = rk_UNCONST(target); type2.targetinfo.data = NULL; type2.targetinfo.length = 0; ret = heim_ntlm_encode_type2(&type2, &data); if (ret) errx(1, "heim_ntlm_encode_type2"); memset(&type2, 0, sizeof(type2)); ret = heim_ntlm_decode_type2(&data, &type2); free(data.data); if (ret) errx(1, "heim_ntlm_decode_type2"); heim_ntlm_free_type2(&type2); /* * */ memset(&type3, 0, sizeof(type3)); type3.flags = flags; type3.username = rk_UNCONST(user); type3.targetname = rk_UNCONST(target); type3.ws = rk_UNCONST("workstation"); { struct ntlm_buf key; heim_ntlm_nt_key(password, &key); heim_ntlm_calculate_ntlm1(key.data, key.length, type2.challenge, &type3.ntlm); free(key.data); } ret = heim_ntlm_encode_type3(&type3, &data); if (ret) errx(1, "heim_ntlm_encode_type3"); free(type3.ntlm.data); memset(&type3, 0, sizeof(type3)); ret = heim_ntlm_decode_type3(&data, 1, &type3); free(data.data); if (ret) errx(1, "heim_ntlm_decode_type3"); if (strcmp("workstation", type3.ws) != 0) errx(1, "type3 ws wrong"); if (strcmp(target, type3.targetname) != 0) errx(1, "type3 targetname wrong"); if (strcmp(user, type3.username) != 0) errx(1, "type3 username wrong"); heim_ntlm_free_type3(&type3); /* * NTLMv2 */ memset(&type2, 0, sizeof(type2)); flags = NTLM_NEG_UNICODE | NTLM_NEG_NTLM | NTLM_TARGET_DOMAIN; type2.flags = flags; memset(type2.challenge, 0x7f, sizeof(type2.challenge)); type2.targetname = rk_UNCONST(target); type2.targetinfo.data = "\x00\x00"; type2.targetinfo.length = 2; ret = heim_ntlm_encode_type2(&type2, &data); if (ret) errx(1, "heim_ntlm_encode_type2"); memset(&type2, 0, sizeof(type2)); ret = heim_ntlm_decode_type2(&data, &type2); free(data.data); if (ret) errx(1, "heim_ntlm_decode_type2"); heim_ntlm_free_type2(&type2); return 0; }
static void client_mschapv2(const void *server_nonce, size_t snoncelen, const void *client_nonce, size_t cnoncelen, const char *username, const char *password) { SHA_CTX ctx; MD4_CTX hctx; unsigned char md[SHA_DIGEST_LENGTH], challange[SHA_DIGEST_LENGTH]; unsigned char hmd[MD4_DIGEST_LENGTH]; struct ntlm_buf answer; int i, len, ret; char *h; SHA1_Init(&ctx); SHA1_Update(&ctx, client_nonce, cnoncelen); SHA1_Update(&ctx, server_nonce, snoncelen); SHA1_Update(&ctx, username, strlen(username)); SHA1_Final(md, &ctx); MD4_Init(&hctx); len = strlen(password); for (i = 0; i < len; i++) { MD4_Update(&hctx, &password[i], 1); MD4_Update(&hctx, &password[len], 1); } MD4_Final(hmd, &hctx); /* ChallengeResponse */ ret = heim_ntlm_calculate_ntlm1(hmd, sizeof(hmd), md, &answer); if (ret) errx(1, "heim_ntlm_calculate_ntlm1"); hex_encode(answer.data, answer.length, &h); printf("responseData=%s\n", h); free(h); /* PasswordHash */ MD4_Init(&hctx); MD4_Update(&hctx, hmd, sizeof(hmd)); MD4_Final(hmd, &hctx); /* GenerateAuthenticatorResponse */ SHA1_Init(&ctx); SHA1_Update(&ctx, hmd, sizeof(hmd)); SHA1_Update(&ctx, answer.data, answer.length); SHA1_Update(&ctx, ms_chap_v2_magic1, sizeof(ms_chap_v2_magic1)); SHA1_Final(md, &ctx); /* ChallengeHash */ SHA1_Init(&ctx); SHA1_Update(&ctx, client_nonce, cnoncelen); SHA1_Update(&ctx, server_nonce, snoncelen); SHA1_Update(&ctx, username, strlen(username)); SHA1_Final(challange, &ctx); SHA1_Init(&ctx); SHA1_Update(&ctx, md, sizeof(md)); SHA1_Update(&ctx, challange, 8); SHA1_Update(&ctx, ms_chap_v2_magic2, sizeof(ms_chap_v2_magic2)); SHA1_Final(md, &ctx); hex_encode(md, sizeof(md), &h); printf("AuthenticatorResponse=%s\n", h); free(h); /* get_master, rfc 3079 3.4 */ SHA1_Init(&ctx); SHA1_Update(&ctx, hmd, sizeof(hmd)); SHA1_Update(&ctx, answer.data, answer.length); SHA1_Update(&ctx, ms_rfc3079_magic1, sizeof(ms_rfc3079_magic1)); SHA1_Final(md, &ctx); free(answer.data); hex_encode(md, 16, &h); printf("session-key=%s\n", h); free(h); }