Exemple #1
0
/*
  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;
}
Exemple #5
0
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;
}