/* parse a negTokenInit packet giving a GUID, a list of supported OIDs (the mechanisms) and a principal name string */ bool spnego_parse_negTokenInit(TALLOC_CTX *ctx, DATA_BLOB blob, char *OIDs[ASN1_MAX_OIDS], char **principal, DATA_BLOB *secblob) { int i; bool ret = false; ASN1_DATA *data; for (i = 0; i < ASN1_MAX_OIDS; i++) { OIDs[i] = NULL; } if (principal) { *principal = NULL; } if (secblob) { *secblob = data_blob_null; } data = asn1_init(talloc_tos()); if (data == NULL) { return false; } if (!asn1_load(data, blob)) goto err; if (!asn1_start_tag(data,ASN1_APPLICATION(0))) goto err; if (!asn1_check_OID(data,OID_SPNEGO)) goto err; /* negTokenInit [0] NegTokenInit */ if (!asn1_start_tag(data,ASN1_CONTEXT(0))) goto err; if (!asn1_start_tag(data,ASN1_SEQUENCE(0))) goto err; /* mechTypes [0] MechTypeList OPTIONAL */ /* Not really optional, we depend on this to decide * what mechanisms we have to work with. */ if (!asn1_start_tag(data,ASN1_CONTEXT(0))) goto err; if (!asn1_start_tag(data,ASN1_SEQUENCE(0))) goto err; for (i=0; asn1_tag_remaining(data) > 0 && i < ASN1_MAX_OIDS-1; i++) { if (!asn1_read_OID(data,ctx, &OIDs[i])) { goto err; } if (asn1_has_error(data)) { goto err; } } OIDs[i] = NULL; if (!asn1_end_tag(data)) goto err; if (!asn1_end_tag(data)) goto err; /* Win7 + Live Sign-in Assistant attaches a mechToken ASN1_CONTEXT(2) to the negTokenInit packet which breaks our negotiation if we just assume the next tag is ASN1_CONTEXT(3). */ if (asn1_peek_tag(data, ASN1_CONTEXT(1))) { uint8_t flags; /* reqFlags [1] ContextFlags OPTIONAL */ if (!asn1_start_tag(data, ASN1_CONTEXT(1))) goto err; if (!asn1_start_tag(data, ASN1_BIT_STRING)) goto err; while (asn1_tag_remaining(data) > 0) { if (!asn1_read_uint8(data, &flags)) goto err; } if (!asn1_end_tag(data)) goto err; if (!asn1_end_tag(data)) goto err; } if (asn1_peek_tag(data, ASN1_CONTEXT(2))) { DATA_BLOB sblob = data_blob_null; /* mechToken [2] OCTET STRING OPTIONAL */ if (!asn1_start_tag(data, ASN1_CONTEXT(2))) goto err; if (!asn1_read_OctetString(data, ctx, &sblob)) goto err; if (!asn1_end_tag(data)) { data_blob_free(&sblob); goto err; } if (secblob) { *secblob = sblob; } else { data_blob_free(&sblob); } } if (asn1_peek_tag(data, ASN1_CONTEXT(3))) { char *princ = NULL; /* mechListMIC [3] OCTET STRING OPTIONAL */ if (!asn1_start_tag(data, ASN1_CONTEXT(3))) goto err; if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) goto err; if (!asn1_start_tag(data, ASN1_CONTEXT(0))) goto err; if (!asn1_read_GeneralString(data, ctx, &princ)) goto err; if (!asn1_end_tag(data)) goto err; if (!asn1_end_tag(data)) goto err; if (!asn1_end_tag(data)) goto err; if (principal) { *principal = princ; } else { TALLOC_FREE(princ); } } if (!asn1_end_tag(data)) goto err; if (!asn1_end_tag(data)) goto err; if (!asn1_end_tag(data)) goto err; ret = !asn1_has_error(data); err: if (asn1_has_error(data)) { int j; if (principal) { TALLOC_FREE(*principal); } if (secblob) { data_blob_free(secblob); } for(j = 0; j < i && j < ASN1_MAX_OIDS-1; j++) { TALLOC_FREE(OIDs[j]); } } asn1_free(data); return ret; }
/* parse a SPNEGO auth packet. This contains the encrypted passwords */ bool spnego_parse_auth_response(TALLOC_CTX *ctx, DATA_BLOB blob, NTSTATUS nt_status, const char *mechOID, DATA_BLOB *auth) { ASN1_DATA *data; uint8 negResult; bool ret = false; if (NT_STATUS_IS_OK(nt_status)) { negResult = SPNEGO_ACCEPT_COMPLETED; } else if (NT_STATUS_EQUAL(nt_status, NT_STATUS_MORE_PROCESSING_REQUIRED)) { negResult = SPNEGO_ACCEPT_INCOMPLETE; } else { negResult = SPNEGO_REJECT; } data = asn1_init(talloc_tos()); if (data == NULL) { return false; } *auth = data_blob_null; if (!asn1_load(data, blob)) goto err; if (!asn1_start_tag(data, ASN1_CONTEXT(1))) goto err; if (!asn1_start_tag(data, ASN1_SEQUENCE(0))) goto err; if (!asn1_start_tag(data, ASN1_CONTEXT(0))) goto err; if (!asn1_check_enumerated(data, negResult)) goto err; if (!asn1_end_tag(data)) goto err; if (asn1_tag_remaining(data)) { if (!asn1_start_tag(data,ASN1_CONTEXT(1))) goto err; if (!asn1_check_OID(data, mechOID)) goto err; if (!asn1_end_tag(data)) goto err; if (asn1_tag_remaining(data)) { if (!asn1_start_tag(data,ASN1_CONTEXT(2))) goto err; if (!asn1_read_OctetString(data, ctx, auth)) goto err; if (!asn1_end_tag(data)) goto err; } } else if (negResult == SPNEGO_ACCEPT_INCOMPLETE) { data->has_error = 1; goto err; } /* Binding against Win2K DC returns a duplicate of the responseToken in * the optional mechListMIC field. This is a bug in Win2K. We ignore * this field if it exists. Win2K8 may return a proper mechListMIC at * which point we need to implement the integrity checking. */ if (asn1_tag_remaining(data)) { DATA_BLOB mechList = data_blob_null; if (!asn1_start_tag(data, ASN1_CONTEXT(3))) goto err; if (!asn1_read_OctetString(data, ctx, &mechList)) goto err; data_blob_free(&mechList); if (!asn1_end_tag(data)) goto err; DEBUG(5,("spnego_parse_auth_response received mechListMIC, " "ignoring.\n")); } if (!asn1_end_tag(data)) goto err; if (!asn1_end_tag(data)) goto err; ret = !data->has_error; err: if (data->has_error) { DEBUG(3,("spnego_parse_auth_response failed at %d\n", (int)data->ofs)); asn1_free(data); data_blob_free(auth); return false; } asn1_free(data); return ret; }
/* This implements kerberos password change protocol as specified in * kerb-chg-password-02.txt and kerberos-set-passwd-02.txt * as well as microsoft version of the protocol * as specified in kerberos-set-passwd-00.txt */ static DATA_BLOB encode_krb5_setpw(const char *principal, const char *password) { char* princ_part1 = NULL; char* princ_part2 = NULL; char* realm = NULL; char* c; char* princ; ASN1_DATA req; DATA_BLOB ret; princ = SMB_STRDUP(principal); if ((c = strchr_m(princ, '/')) == NULL) { c = princ; } else { *c = '\0'; c++; princ_part1 = princ; } princ_part2 = c; if ((c = strchr_m(c, '@')) != NULL) { *c = '\0'; c++; realm = c; } else { /* We must have a realm component. */ return data_blob_null; } memset(&req, 0, sizeof(req)); asn1_push_tag(&req, ASN1_SEQUENCE(0)); asn1_push_tag(&req, ASN1_CONTEXT(0)); asn1_write_OctetString(&req, password, strlen(password)); asn1_pop_tag(&req); asn1_push_tag(&req, ASN1_CONTEXT(1)); asn1_push_tag(&req, ASN1_SEQUENCE(0)); asn1_push_tag(&req, ASN1_CONTEXT(0)); asn1_write_Integer(&req, 1); asn1_pop_tag(&req); asn1_push_tag(&req, ASN1_CONTEXT(1)); asn1_push_tag(&req, ASN1_SEQUENCE(0)); if (princ_part1) { asn1_write_GeneralString(&req, princ_part1); } asn1_write_GeneralString(&req, princ_part2); asn1_pop_tag(&req); asn1_pop_tag(&req); asn1_pop_tag(&req); asn1_pop_tag(&req); asn1_push_tag(&req, ASN1_CONTEXT(2)); asn1_write_GeneralString(&req, realm); asn1_pop_tag(&req); asn1_pop_tag(&req); ret = data_blob(req.data, req.length); asn1_free(&req); free(princ); return ret; }
DATA_BLOB spnego_gen_negTokenInit(TALLOC_CTX *ctx, const char *OIDs[], DATA_BLOB *psecblob, const char *principal) { int i; ASN1_DATA *data; DATA_BLOB ret = data_blob_null; data = asn1_init(talloc_tos()); if (data == NULL) { return data_blob_null; } if (!asn1_push_tag(data,ASN1_APPLICATION(0))) goto err; if (!asn1_write_OID(data,OID_SPNEGO)) goto err; if (!asn1_push_tag(data,ASN1_CONTEXT(0))) goto err; if (!asn1_push_tag(data,ASN1_SEQUENCE(0))) goto err; if (!asn1_push_tag(data,ASN1_CONTEXT(0))) goto err; if (!asn1_push_tag(data,ASN1_SEQUENCE(0))) goto err; for (i=0; OIDs[i]; i++) { if (!asn1_write_OID(data,OIDs[i])) goto err; } if (!asn1_pop_tag(data)) goto err; if (!asn1_pop_tag(data)) goto err; if (psecblob && psecblob->length && psecblob->data) { if (!asn1_push_tag(data, ASN1_CONTEXT(2))) goto err; if (!asn1_write_OctetString(data,psecblob->data, psecblob->length)) goto err; if (!asn1_pop_tag(data)) goto err; } if (principal) { if (!asn1_push_tag(data, ASN1_CONTEXT(3))) goto err; if (!asn1_push_tag(data, ASN1_SEQUENCE(0))) goto err; if (!asn1_push_tag(data, ASN1_CONTEXT(0))) goto err; if (!asn1_write_GeneralString(data,principal)) goto err; if (!asn1_pop_tag(data)) goto err; if (!asn1_pop_tag(data)) goto err; if (!asn1_pop_tag(data)) goto err; } if (!asn1_pop_tag(data)) goto err; if (!asn1_pop_tag(data)) goto err; if (!asn1_pop_tag(data)) goto err; ret = data_blob_talloc(ctx, data->data, data->length); err: if (data->has_error) { DEBUG(1,("Failed to build negTokenInit at offset %d\n", (int)data->ofs)); } asn1_free(data); return ret; }
static NTSTATUS check_spnego_blob_complete(uint16 smbpid, uint16 vuid, DATA_BLOB *pblob) { struct pending_auth_data *pad = NULL; ASN1_DATA data; size_t needed_len = 0; pad = get_pending_auth_data(smbpid); /* Ensure we have some data. */ if (pblob->length == 0) { /* Caller can cope. */ DEBUG(2,("check_spnego_blob_complete: zero blob length !\n")); delete_partial_auth(pad); return NT_STATUS_OK; } /* Were we waiting for more data ? */ if (pad) { DATA_BLOB tmp_blob; size_t copy_len = MIN(65536, pblob->length); /* Integer wrap paranoia.... */ if (pad->partial_data.length + copy_len < pad->partial_data.length || pad->partial_data.length + copy_len < copy_len) { DEBUG(2,("check_spnego_blob_complete: integer wrap " "pad->partial_data.length = %u, " "copy_len = %u\n", (unsigned int)pad->partial_data.length, (unsigned int)copy_len )); delete_partial_auth(pad); return NT_STATUS_INVALID_PARAMETER; } DEBUG(10,("check_spnego_blob_complete: " "pad->partial_data.length = %u, " "pad->needed_len = %u, " "copy_len = %u, " "pblob->length = %u,\n", (unsigned int)pad->partial_data.length, (unsigned int)pad->needed_len, (unsigned int)copy_len, (unsigned int)pblob->length )); tmp_blob = data_blob(NULL, pad->partial_data.length + copy_len); /* Concatenate the two (up to copy_len) bytes. */ memcpy(tmp_blob.data, pad->partial_data.data, pad->partial_data.length); memcpy(tmp_blob.data + pad->partial_data.length, pblob->data, copy_len); /* Replace the partial data. */ data_blob_free(&pad->partial_data); pad->partial_data = tmp_blob; ZERO_STRUCT(tmp_blob); /* Are we done ? */ if (pblob->length >= pad->needed_len) { /* Yes, replace pblob. */ data_blob_free(pblob); *pblob = pad->partial_data; ZERO_STRUCT(pad->partial_data); delete_partial_auth(pad); return NT_STATUS_OK; } /* Still need more data. */ pad->needed_len -= copy_len; return NT_STATUS_MORE_PROCESSING_REQUIRED; } if ((pblob->data[0] != ASN1_APPLICATION(0)) && (pblob->data[0] != ASN1_CONTEXT(1))) { /* Not something we can determine the * length of. */ return NT_STATUS_OK; } /* This is a new SPNEGO sessionsetup - see if * the data given in this blob is enough. */ asn1_load(&data, *pblob); asn1_start_tag(&data, pblob->data[0]); if (data.has_error || data.nesting == NULL) { asn1_free(&data); /* Let caller catch. */ return NT_STATUS_OK; } /* Integer wrap paranoia.... */ if (data.nesting->taglen + data.nesting->start < data.nesting->taglen || data.nesting->taglen + data.nesting->start < data.nesting->start) { DEBUG(2,("check_spnego_blob_complete: integer wrap " "data.nesting->taglen = %u, " "data.nesting->start = %u\n", (unsigned int)data.nesting->taglen, (unsigned int)data.nesting->start )); asn1_free(&data); return NT_STATUS_INVALID_PARAMETER; } /* Total length of the needed asn1 is the tag length * plus the current offset. */ needed_len = data.nesting->taglen + data.nesting->start; asn1_free(&data); DEBUG(10,("check_spnego_blob_complete: needed_len = %u, " "pblob->length = %u\n", (unsigned int)needed_len, (unsigned int)pblob->length )); if (needed_len <= pblob->length) { /* Nothing to do - blob is complete. */ return NT_STATUS_OK; } /* Refuse the blob if it's bigger than 64k. */ if (needed_len > 65536) { DEBUG(2,("check_spnego_blob_complete: needed_len too large (%u)\n", (unsigned int)needed_len )); return NT_STATUS_INVALID_PARAMETER; } /* We must store this blob until complete. */ pad = SMB_MALLOC(sizeof(struct pending_auth_data)); if (!pad) { return NT_STATUS_NO_MEMORY; } pad->needed_len = needed_len - pblob->length; pad->partial_data = data_blob(pblob->data, pblob->length); if (pad->partial_data.data == NULL) { SAFE_FREE(pad); return NT_STATUS_NO_MEMORY; } pad->smbpid = smbpid; pad->vuid = vuid; DLIST_ADD(pd_list, pad); return NT_STATUS_MORE_PROCESSING_REQUIRED; }