summaryrefslogtreecommitdiff
path: root/source4/libcli/auth
diff options
context:
space:
mode:
Diffstat (limited to 'source4/libcli/auth')
-rw-r--r--source4/libcli/auth/gensec_krb5.c264
-rw-r--r--source4/libcli/auth/kerberos.h7
-rw-r--r--source4/libcli/auth/kerberos_verify.c75
3 files changed, 318 insertions, 28 deletions
diff --git a/source4/libcli/auth/gensec_krb5.c b/source4/libcli/auth/gensec_krb5.c
index f5f02d1421..260b256934 100644
--- a/source4/libcli/auth/gensec_krb5.c
+++ b/source4/libcli/auth/gensec_krb5.c
@@ -38,14 +38,167 @@ enum GENSEC_KRB5_STATE {
struct gensec_krb5_state {
TALLOC_CTX *mem_ctx;
DATA_BLOB session_key;
- DATA_BLOB pac;
+ struct PAC_LOGON_INFO logon_info;
enum GENSEC_KRB5_STATE state_position;
krb5_context krb5_context;
krb5_auth_context krb5_auth_context;
krb5_ccache krb5_ccache;
krb5_data ticket;
+ krb5_keyblock krb5_keyblock;
};
+static NTSTATUS gensec_krb5_pac_checksum(DATA_BLOB pac_data,
+ struct PAC_SIGNATURE_DATA *sig,
+ struct gensec_krb5_state *gensec_krb5_state,
+ uint32 cksum_type)
+{
+ krb5_error_code ret;
+ krb5_crypto crypto;
+ Checksum cksum;
+
+ cksum.cksumtype = (CKSUMTYPE)sig->type;
+ cksum.checksum.length = sizeof(sig->signature);
+ cksum.checksum.data = sig->signature;
+
+
+ ret = krb5_crypto_init(gensec_krb5_state->krb5_context,
+ &gensec_krb5_state->krb5_keyblock,
+ cksum_type,
+ &crypto);
+ if (ret) {
+ DEBUG(0,("krb5_crypto_init() failed\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ ret = krb5_verify_checksum(gensec_krb5_state->krb5_context,
+ crypto,
+ cksum_type,
+ pac_data.data,
+ pac_data.length,
+ &cksum);
+
+ krb5_crypto_destroy(gensec_krb5_state->krb5_context, crypto);
+
+ if (ret) {
+ DEBUG(0,("NOT verifying PAC checksums yet!\n"));
+ //return NT_STATUS_LOGON_FAILURE;
+ } else {
+ DEBUG(0,("PAC checksums verified!\n"));
+ }
+
+ return NT_STATUS_OK;
+}
+
+NTSTATUS gensec_krb5_decode_pac(TALLOC_CTX *mem_ctx,
+ struct PAC_LOGON_INFO *logon_info_out,
+ DATA_BLOB blob,
+ struct gensec_krb5_state *gensec_krb5_state)
+{
+ NTSTATUS status;
+ struct PAC_SIGNATURE_DATA srv_sig;
+ uint8_t *srv_key = NULL;
+ struct PAC_SIGNATURE_DATA kdc_sig;
+ uint8_t *kdc_key = NULL;
+ struct PAC_LOGON_INFO *logon_info = NULL;
+ struct PAC_DATA pac_data;
+ DATA_BLOB tmp_blob;
+ int i;
+
+ status = ndr_pull_struct_blob(&blob, mem_ctx, &pac_data,
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_DATA);
+ if (!NT_STATUS_IS_OK(status)) {
+ DEBUG(0,("can't parse the PAC\n"));
+ return status;
+ }
+
+ if (pac_data.num_buffers < 3) {
+ /* we need logon_ingo, service_key and kdc_key */
+ DEBUG(0,("less than 3 PAC buffers\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ for (i=0; i < pac_data.num_buffers; i++) {
+ switch (pac_data.buffers[i].type) {
+ case PAC_TYPE_LOGON_INFO:
+ if (!pac_data.buffers[i].info) {
+ break;
+ }
+ logon_info = &pac_data.buffers[i].info->logon_info;
+ break;
+ case PAC_TYPE_SRV_CHECKSUM:
+ if (!pac_data.buffers[i].info) {
+ break;
+ }
+ srv_key = (uint8_t *)&pac_data.buffers[i].info->srv_cksum.signature;
+ srv_sig = pac_data.buffers[i].info->srv_cksum;
+ break;
+ case PAC_TYPE_KDC_CHECKSUM:
+ if (!pac_data.buffers[i].info) {
+ break;
+ }
+ kdc_key = (uint8_t *)&pac_data.buffers[i].info->kdc_cksum.signature;
+ kdc_sig = pac_data.buffers[i].info->kdc_cksum;
+ break;
+ case PAC_TYPE_UNKNOWN_10:
+ break;
+ default:
+ break;
+ }
+ }
+
+ if (!logon_info) {
+ DEBUG(0,("PAC no logon_info\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (!srv_key) {
+ DEBUG(0,("PAC no srv_key\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ if (!kdc_key) {
+ DEBUG(0,("PAC no kdc_key\n"));
+ return NT_STATUS_FOOBAR;
+ }
+
+ /* clear the kdc_key */
+ memset(kdc_key , '\0', 16);
+
+ status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data,
+ (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* verify by kdc_key */
+ status = gensec_krb5_pac_checksum(tmp_blob, &kdc_sig, gensec_krb5_state, 0);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* clear the service_key */
+ memset(srv_key , '\0', 16);
+
+ status = ndr_push_struct_blob(&tmp_blob, mem_ctx, &pac_data,
+ (ndr_push_flags_fn_t)ndr_push_PAC_DATA);
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ /* verify by servie_key */
+ status = gensec_krb5_pac_checksum(tmp_blob, &srv_sig, gensec_krb5_state, 0);
+
+ if (!NT_STATUS_IS_OK(status)) {
+ return status;
+ }
+
+ DEBUG(0,("account_name: %s [%s]\n",logon_info->account_name.string, logon_info->full_name.string));
+ *logon_info_out = *logon_info;
+
+ return status;
+}
+
static NTSTATUS gensec_krb5_start(struct gensec_security *gensec_security)
{
struct gensec_krb5_state *gensec_krb5_state;
@@ -334,7 +487,7 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL
}
return nt_status;
}
-
+
case GENSEC_KRB5_SERVER_START:
{
char *principal;
@@ -348,19 +501,25 @@ static NTSTATUS gensec_krb5_update(struct gensec_security *gensec_security, TALL
gensec_krb5_state->krb5_context,
gensec_krb5_state->krb5_auth_context,
lp_realm(), &in,
- &principal, &pac, &unwrapped_out);
+ &principal, &pac, &unwrapped_out,
+ &gensec_krb5_state->krb5_keyblock);
} else {
/* TODO: check the tok_id */
nt_status = ads_verify_ticket(out_mem_ctx,
gensec_krb5_state->krb5_context,
gensec_krb5_state->krb5_auth_context,
lp_realm(), &unwrapped_in,
- &principal, &pac, &unwrapped_out);
+ &principal, &pac, &unwrapped_out,
+ &gensec_krb5_state->krb5_keyblock);
}
- gensec_krb5_state->pac = data_blob_talloc_steal(out_mem_ctx, gensec_krb5_state->mem_ctx,
- &pac);
- /* TODO: parse the pac */
+ if (!NT_STATUS_IS_OK(nt_status)) {
+ return nt_status;
+ }
+
+ /* decode and verify the pac */
+ nt_status = gensec_krb5_decode_pac(gensec_krb5_state->mem_ctx, &gensec_krb5_state->logon_info, pac,
+ gensec_krb5_state);
if (NT_STATUS_IS_OK(nt_status)) {
gensec_krb5_state->state_position = GENSEC_KRB5_DONE;
@@ -413,6 +572,95 @@ static NTSTATUS gensec_krb5_session_key(struct gensec_security *gensec_security,
return NT_STATUS_NO_USER_SESSION_KEY;
}
}
+/*
+struct gensec_krb5_state {
+ TALLOC_CTX *mem_ctx;
+ DATA_BLOB session_key;
+ struct PAC_LOGON_INFO logon_info;
+ enum GENSEC_KRB5_STATE state_position;
+ krb5_context krb5_context;
+ krb5_auth_context krb5_auth_context;
+ krb5_ccache krb5_ccache;
+ krb5_data ticket;
+ krb5_keyblock krb5_keyblock;
+};
+struct auth_session_info
+{
+ TALLOC_CTX *mem_ctx;
+
+ int refcount;
+
+ NT_USER_TOKEN *nt_user_token;
+
+ struct auth_serversupplied_info *server_info;
+
+ DATA_BLOB session_key;
+
+ const char *workstation;
+};
+*/
+static NTSTATUS gensec_krb5_session_info(struct gensec_security *gensec_security,
+ struct auth_session_info **session_info_out)
+{
+ struct gensec_krb5_state *gensec_krb5_state = gensec_security->private_data;
+ TALLOC_CTX *mem_ctx;
+ struct auth_session_info *session_info = NULL;
+ struct PAC_LOGON_INFO *logon_info = &gensec_krb5_state->logon_info;
+ struct nt_user_token *ptoken;
+ struct dom_sid *sid;
+
+ *session_info_out = NULL;
+
+ mem_ctx = talloc_init("krb5: session_info");
+
+ session_info = talloc_p(mem_ctx, struct auth_session_info);
+ if (!session_info) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ session_info->mem_ctx = mem_ctx;
+ session_info->refcount = 1;
+ session_info->server_info = NULL;
+
+ ptoken = talloc_p(session_info->mem_ctx, struct nt_user_token);
+ if (!ptoken) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+ ptoken->num_sids = 0;
+
+ ptoken->user_sids = talloc_array_p(mem_ctx, struct dom_sid*, logon_info->groups_count + 2);
+ if (!ptoken->user_sids) {
+ return NT_STATUS_NO_MEMORY;
+ }
+
+
+ sid = dom_sid_dup(session_info->mem_ctx, logon_info->dom_sid);
+ ptoken->user_sids[0] = dom_sid_add_rid(session_info->mem_ctx, sid, logon_info->user_rid);
+ ptoken->num_sids++;
+ sid = dom_sid_dup(session_info->mem_ctx, logon_info->dom_sid);
+ ptoken->user_sids[1] = dom_sid_add_rid(session_info->mem_ctx, sid, logon_info->group_rid);
+ ptoken->num_sids++;
+
+ for (;ptoken->num_sids < logon_info->groups_count; ptoken->num_sids++) {
+ sid = dom_sid_dup(session_info->mem_ctx, logon_info->dom_sid);
+ ptoken->user_sids[ptoken->num_sids] = dom_sid_add_rid(session_info->mem_ctx, sid, logon_info->groups[ptoken->num_sids - 2].rid);
+ }
+
+ debug_nt_user_token(DBGC_AUTH, 0, ptoken);
+
+ session_info->nt_user_token = ptoken;
+
+ session_info->session_key = data_blob_talloc(session_info->mem_ctx,
+ gensec_krb5_state->session_key.data,
+ gensec_krb5_state->session_key.length);
+
+ session_info->workstation = NULL;
+
+ *session_info_out = session_info;
+
+ return NT_STATUS_OK;
+}
static const struct gensec_security_ops gensec_krb5_security_ops = {
@@ -423,6 +671,7 @@ static const struct gensec_security_ops gensec_krb5_security_ops = {
.server_start = gensec_krb5_server_start,
.update = gensec_krb5_update,
.session_key = gensec_krb5_session_key,
+ .session_info = gensec_krb5_session_info,
.end = gensec_krb5_end
};
@@ -434,6 +683,7 @@ static const struct gensec_security_ops gensec_ms_krb5_security_ops = {
.server_start = gensec_krb5_server_start,
.update = gensec_krb5_update,
.session_key = gensec_krb5_session_key,
+ .session_info = gensec_krb5_session_info,
.end = gensec_krb5_end
};
diff --git a/source4/libcli/auth/kerberos.h b/source4/libcli/auth/kerberos.h
index ca796d0c86..e35079a4ee 100644
--- a/source4/libcli/auth/kerberos.h
+++ b/source4/libcli/auth/kerberos.h
@@ -47,6 +47,10 @@ krb5_error_code krb5_auth_con_setuseruserkey(krb5_context context, krb5_auth_con
void krb5_free_unparsed_name(krb5_context ctx, char *val);
#endif
+#if defined(HAVE_KRB5_PRINCIPAL_GET_COMP_STRING) && !defined(HAVE_KRB5_PRINC_COMPONENT)
+const krb5_data *krb5_princ_component(krb5_context context, krb5_principal principal, int i );
+#endif
+
/* Samba wrapper function for krb5 functionality. */
void setup_kaddr( krb5_address *pkaddr, struct sockaddr *paddr);
int create_kerberos_key_from_string(krb5_context context, krb5_principal host_princ, krb5_data *password, krb5_keyblock *key, krb5_enctype enctype);
@@ -68,7 +72,8 @@ NTSTATUS ads_verify_ticket(TALLOC_CTX *mem_ctx,
krb5_auth_context auth_context,
const char *realm, const DATA_BLOB *ticket,
char **principal, DATA_BLOB *auth_data,
- DATA_BLOB *ap_rep);
+ DATA_BLOB *ap_rep,
+ krb5_keyblock *keyblock);
int kerberos_kinit_password_cc(krb5_context ctx, krb5_ccache cc, const char *principal, const char *password, time_t *expire_time, time_t *kdc_time);
#endif /* HAVE_KRB5 */
diff --git a/source4/libcli/auth/kerberos_verify.c b/source4/libcli/auth/kerberos_verify.c
index 5cd22b3749..d1f0433ccc 100644
--- a/source4/libcli/auth/kerberos_verify.c
+++ b/source4/libcli/auth/kerberos_verify.c
@@ -26,6 +26,33 @@
#ifdef HAVE_KRB5
+static DATA_BLOB unwrap_pac(TALLOC_CTX *mem_ctx, DATA_BLOB *auth_data)
+{
+ DATA_BLOB out;
+ DATA_BLOB pac_contents = data_blob(NULL, 0);
+ ASN1_DATA data;
+ int data_type;
+
+ asn1_load(&data, *auth_data);
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_SEQUENCE(0));
+ asn1_start_tag(&data, ASN1_CONTEXT(0));
+ asn1_read_Integer(&data, &data_type);
+ asn1_end_tag(&data);
+ asn1_start_tag(&data, ASN1_CONTEXT(1));
+ asn1_read_OctetString(&data, &pac_contents);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_end_tag(&data);
+ asn1_free(&data);
+
+ out = data_blob_talloc(mem_ctx, pac_contents.data, pac_contents.length);
+
+ data_blob_free(&pac_contents);
+
+ return out;
+}
+
/**********************************************************************************
Try to verify a ticket using the system keytab... the system keytab has kvno -1 entries, so
it's more like what microsoft does... see comment in utils/net_ads.c in the
@@ -33,7 +60,8 @@
***********************************************************************************/
static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context auth_context,
- const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt)
+ const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt,
+ krb5_keyblock *keyblock)
{
krb5_error_code ret = 0;
BOOL auth_ok = False;
@@ -46,6 +74,8 @@ static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context aut
ZERO_STRUCT(kt_entry);
ZERO_STRUCT(cursor);
+ ZERO_STRUCTP(keyblock);
+
ret = krb5_kt_default(context, &keytab);
if (ret) {
DEBUG(1, ("ads_keytab_verify_ticket: krb5_kt_default failed (%s)\n", error_message(ret)));
@@ -75,17 +105,22 @@ static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context aut
p_packet->length = ticket->length;
p_packet->data = (krb5_pointer)ticket->data;
- if (!(ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt))) {
+ ret = krb5_rd_req(context, &auth_context, p_packet, NULL, NULL, NULL, pp_tkt);
+ if (!ret) {
unsigned int keytype;
krb5_free_unparsed_name(context, princ_name);
princ_name = NULL;
#ifdef HAVE_KRB5_KEYTAB_ENTRY_KEYBLOCK
keytype = (unsigned int) kt_entry.keyblock.keytype;
+ copy_EncryptionKey(&kt_entry.keyblock, keyblock);
#else
keytype = (unsigned int) kt_entry.key.enctype;
+ /* I'not sure if that works --metze*/
+ copy_EncryptionKey(&kt_entry.key, keyblock);
#endif
DEBUG(10,("ads_keytab_verify_ticket: enc type [%u] decrypted message !\n",
keytype));
+
auth_ok = True;
break;
}
@@ -124,7 +159,8 @@ static BOOL ads_keytab_verify_ticket(krb5_context context, krb5_auth_context aut
static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context auth_context,
krb5_principal host_princ,
- const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt)
+ const DATA_BLOB *ticket, krb5_data *p_packet, krb5_ticket **pp_tkt,
+ krb5_keyblock *keyblock)
{
krb5_error_code ret = 0;
BOOL auth_ok = False;
@@ -133,6 +169,8 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au
krb5_enctype *enctypes = NULL;
int i;
+ ZERO_STRUCTP(keyblock);
+
if (!secrets_init()) {
DEBUG(1,("ads_secrets_verify_ticket: secrets_init failed\n"));
return False;
@@ -160,30 +198,24 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au
/* We need to setup a auth context with each possible encoding type in turn. */
for (i=0;enctypes[i];i++) {
- krb5_keyblock *key = NULL;
-
- if (!(key = (krb5_keyblock *)malloc(sizeof(*key)))) {
- goto out;
- }
-
- if (create_kerberos_key_from_string(context, host_princ, &password, key, enctypes[i])) {
- SAFE_FREE(key);
+ if (create_kerberos_key_from_string(context, host_princ, &password, keyblock, enctypes[i])) {
continue;
}
- krb5_auth_con_setuseruserkey(context, auth_context, key);
-
- krb5_free_keyblock(context, key);
+ krb5_auth_con_setuseruserkey(context, auth_context, keyblock);
- if (!(ret = krb5_rd_req(context, &auth_context, p_packet,
+ ret = krb5_rd_req(context, &auth_context, p_packet,
NULL,
- NULL, NULL, pp_tkt))) {
+ NULL, NULL, pp_tkt);
+ if (!ret) {
DEBUG(10,("ads_secrets_verify_ticket: enc type [%u] decrypted message !\n",
(unsigned int)enctypes[i] ));
auth_ok = True;
break;
}
-
+
+ free_EncryptionKey(keyblock);
+
DEBUG((ret != KRB5_BAD_ENCTYPE) ? 3 : 10,
("ads_secrets_verify_ticket: enc type [%u] failed to decrypt with error %s\n",
(unsigned int)enctypes[i], error_message(ret)));
@@ -207,7 +239,8 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au
krb5_auth_context auth_context,
const char *realm, const DATA_BLOB *ticket,
char **principal, DATA_BLOB *auth_data,
- DATA_BLOB *ap_rep)
+ DATA_BLOB *ap_rep,
+ krb5_keyblock *keyblock)
{
NTSTATUS sret = NT_STATUS_LOGON_FAILURE;
krb5_data packet;
@@ -267,10 +300,10 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au
goto out;
}
- auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt);
+ auth_ok = ads_keytab_verify_ticket(context, auth_context, ticket, &packet, &tkt, keyblock);
if (!auth_ok) {
auth_ok = ads_secrets_verify_ticket(context, auth_context, host_princ,
- ticket, &packet, &tkt);
+ ticket, &packet, &tkt, keyblock);
}
release_server_mutex();
@@ -299,6 +332,8 @@ static BOOL ads_secrets_verify_ticket(krb5_context context, krb5_auth_context au
get_auth_data_from_tkt(mem_ctx, auth_data, tkt);
+ *auth_data = unwrap_pac(mem_ctx, auth_data);
+
#if 0
if (tkt->enc_part2) {
file_save("/tmp/authdata.dat",