/** * Covert AD object's SID to an ASCII string. * * @param s Bytes to convert. * @param out Converted string. * @return 0 on success; error code on failure. */ DWORD Sid2Str(IN PVOID s, OUT PSTR *out) { DWORD dwError = 0; size_t size = 0; INT count = 0; ULONG i = 0; SidTP sid = (SidTP) s; *out = NULL; size = 2 + 3 + 1 + 14 + (1 + 10) * sid->SubAuthorityCount + 1; dwError = LwAllocateMemory(size * sizeof(char), OUT_PPVOID(out)); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); if (sid->IdentifierAuthority[0] || sid->IdentifierAuthority[1]) { count += snprintf(*out + count, size - count, "S-%u-0x%.2X%.2X%.2X%.2X%.2X%.2X", sid->Revision, sid->IdentifierAuthority[0], sid->IdentifierAuthority[1], sid->IdentifierAuthority[2], sid->IdentifierAuthority[3], sid->IdentifierAuthority[4], sid->IdentifierAuthority[5]); } else { ULONG value = 0; value |= (ULONG) sid->IdentifierAuthority[5]; value |= (ULONG) sid->IdentifierAuthority[4] << 8; value |= (ULONG) sid->IdentifierAuthority[3] << 16; value |= (ULONG) sid->IdentifierAuthority[2] << 24; count += snprintf(*out + count, size - count, "S-%u-%u", sid->Revision, value); } for (i = 0; i < sid->SubAuthorityCount; i++) { #if defined(WORDS_BIGENDIAN) sid->SubAuthority[i] = LW_ENDIAN_SWAP32(sid->SubAuthority[i]); #endif count += snprintf(*out + count, size - count, "-%u", sid->SubAuthority[i]); } cleanup: return dwError; error: LW_SAFE_FREE_MEMORY(*out); goto cleanup; }
DWORD LWNetReadLEDword( OUT PDWORD pdwDest, IN PACKED_ARRAY* pArray ) { if (pArray->totalSize + pArray->pStart - pArray->pCur < sizeof(DWORD)) { return DNS_ERROR_BAD_PACKET; } #if defined(WORDS_BIGENDIAN) *pdwDest = LW_ENDIAN_SWAP32(LWNetReadUnalignedDword(pArray->pCur)); #else *pdwDest = LWNetReadUnalignedDword(pArray->pCur); #endif pArray->pCur += sizeof(DWORD); return ERROR_SUCCESS; }
/** * Generate UID from SID. * * @param s SID bytes. * @param out UID (dynamically allocated). * @return 0 on success; error code on failure. */ DWORD Sid2Id(IN PVOID s, OUT PDWORD out) { DWORD dwError = 0; size_t size = 0; SidTP sid = (SidTP) s; PDWORD subs = NULL; *out = 0; if (sid->Revision != 1) { dwError = ADT_ERR_INVALID_SID; ADT_BAIL_ON_ERROR_NP(dwError); } size = sid->SubAuthorityCount * sizeof(DWORD); dwError = LwAllocateMemory(size, OUT_PPVOID(&subs)); ADT_BAIL_ON_ALLOC_FAILURE_NP(!dwError); memcpy((PVOID)subs, (PVOID) sid->SubAuthority, size); #if defined(WORDS_BIGENDIAN) INT i; for (i = 0; i < sid->SubAuthorityCount; i++) { subs[i] = LW_ENDIAN_SWAP32(subs[i]); } #endif LwUidHashCalc(subs, sid->SubAuthorityCount, out); cleanup: LW_SAFE_FREE_MEMORY(subs); return dwError; error: goto cleanup; }
DWORD LwKrb5VerifyPac( krb5_context ctx, const krb5_ticket *pTgsTicket, const struct berval *pPacBerVal, const krb5_keyblock *serviceKey, char** ppchLogonInfo, size_t* psLogonInfo ) { krb5_error_code ret = 0; PAC_DATA *pPacData = NULL; DWORD i; char *pchPacCopy = NULL; //Do not free krb5_data krbPacData = {0}; //Do not free krb5_checksum checksum = {0}; //Do not free PAC_SIGNATURE_DATA *pServerSig = NULL; PAC_LOGON_NAME *pLogonName = NULL; size_t sServerSig = 0; //Do not free char *pchLogonInfoStart = NULL; size_t sLogonInfoLen = 0; krb5_boolean bHasGoodChecksum = FALSE; uint64_t qwNtAuthTime; DWORD dwError = LW_ERROR_SUCCESS; //Free with krb5_free_unparsed_name PSTR pszClientPrincipal = NULL; PSTR pszLogonName = NULL; char* pchLogonInfo = NULL; #if defined(WORDS_BIGENDIAN) WORD * pwNameLocal = NULL; DWORD dwCount = 0; #endif dwError = LwAllocateMemory( pPacBerVal->bv_len, OUT_PPVOID(&pPacData)); BAIL_ON_LW_ERROR(dwError); memcpy(pPacData, pPacBerVal->bv_val, pPacBerVal->bv_len); #if defined(WORDS_BIGENDIAN) pPacData->dwBufferCount = LW_ENDIAN_SWAP32(pPacData->dwBufferCount); pPacData->dwVersion = LW_ENDIAN_SWAP32(pPacData->dwVersion); #endif // We only know about version 0 if (pPacData->dwVersion != 0) { dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } // Make sure that the last buffer in the pac data doesn't go out of bounds // of the parent buffer if ((void *)&pPacData->buffers[pPacData->dwBufferCount] - (void *)pPacData > pPacBerVal->bv_len) { dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } // Make sure the data associated with each buffer doesn't go out of // bounds for (i = 0; i < pPacData->dwBufferCount; i++) { #if defined(WORDS_BIGENDIAN) pPacData->buffers[i].dwType = LW_ENDIAN_SWAP32(pPacData->buffers[i].dwType); pPacData->buffers[i].dwSize = LW_ENDIAN_SWAP32(pPacData->buffers[i].dwSize); pPacData->buffers[i].qwOffset = LW_ENDIAN_SWAP64(pPacData->buffers[i].qwOffset); #endif if (pPacData->buffers[i].qwOffset + pPacData->buffers[i].dwSize < pPacData->buffers[i].qwOffset) { dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } if (pPacData->buffers[i].qwOffset + pPacData->buffers[i].dwSize > pPacBerVal->bv_len) { dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } } dwError = LwAllocateMemory( pPacBerVal->bv_len, OUT_PPVOID(&pchPacCopy)); BAIL_ON_LW_ERROR(dwError); memcpy(pchPacCopy, pPacBerVal->bv_val, pPacBerVal->bv_len); krbPacData.magic = KV5M_DATA; krbPacData.length = pPacBerVal->bv_len; krbPacData.data = pchPacCopy; for (i = 0; i < pPacData->dwBufferCount; i++) { switch (pPacData->buffers[i].dwType) { case PAC_TYPE_LOGON_INFO: pchLogonInfoStart = (char *)pPacData + pPacData->buffers[i].qwOffset; sLogonInfoLen = pPacData->buffers[i].dwSize; break; case PAC_TYPE_SRV_CHECKSUM: pServerSig = (PAC_SIGNATURE_DATA *)((char *)pPacData + pPacData->buffers[i].qwOffset); #if defined(WORDS_BIGENDIAN) pServerSig->dwType = LW_ENDIAN_SWAP32(pServerSig->dwType); #endif sServerSig = pPacData->buffers[i].dwSize - (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature; /* The checksum is calculated with the signatures zeroed out. */ memset(pchPacCopy + pPacData->buffers[i].qwOffset + (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature, 0, pPacData->buffers[i].dwSize - (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature); break; case PAC_TYPE_KDC_CHECKSUM: /* The checksum is calculated with the signatures zeroed out. */ memset(pchPacCopy + pPacData->buffers[i].qwOffset + (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature, 0, pPacData->buffers[i].dwSize - (size_t)&((PAC_SIGNATURE_DATA *)0)->pchSignature); break; case PAC_TYPE_LOGON_NAME: pLogonName = (PAC_LOGON_NAME *)((char *)pPacData + pPacData->buffers[i].qwOffset); #if defined(WORDS_BIGENDIAN) pLogonName->ticketTime = LW_ENDIAN_SWAP64(pLogonName->ticketTime); pLogonName->wAccountNameLen = LW_ENDIAN_SWAP16(pLogonName->wAccountNameLen); pwNameLocal = pLogonName->pwszName; for ( dwCount = 0 ; dwCount < pLogonName->wAccountNameLen / 2 ; dwCount++ ) { pwNameLocal[dwCount] = LW_ENDIAN_SWAP16(pwNameLocal[dwCount]); } #endif if ((char *)&pLogonName->pwszName + pLogonName->wAccountNameLen > (char *)pPacData + pPacData->buffers[i].qwOffset + pPacData->buffers[i].dwSize) { // The message is invalid because the terminating null // of the name lands outside of the buffer. dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } break; default: break; } } if (pServerSig == NULL) { dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } if (pLogonName == NULL) { //We need the logon name to verify the pac is for the right user dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } if (pchLogonInfoStart == NULL) { /* The buffer we really care about isn't in the pac. */ dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } checksum.magic = KV5M_CHECKSUM; checksum.checksum_type = pServerSig->dwType; checksum.length = sServerSig; checksum.contents = (unsigned char *)pServerSig->pchSignature; ret = krb5_c_verify_checksum( ctx, serviceKey, KRB5_KEYUSAGE_APP_DATA_CKSUM, &krbPacData, &checksum, &bHasGoodChecksum); BAIL_ON_KRB_ERROR(ctx, ret); if (!bHasGoodChecksum) { dwError = LW_ERROR_INVALID_MESSAGE; BAIL_ON_LW_ERROR(dwError); } // Make sure the pac was issued with this ticket, not an old ticket qwNtAuthTime = pTgsTicket->enc_part2->times.authtime; qwNtAuthTime += 11644473600LL; qwNtAuthTime *= 1000*1000*10; if (pLogonName->ticketTime != qwNtAuthTime) { dwError = LW_ERROR_CLOCK_SKEW; BAIL_ON_LW_ERROR(dwError); } ret = krb5_unparse_name( ctx, pTgsTicket->enc_part2->client, &pszClientPrincipal); BAIL_ON_KRB_ERROR(ctx, ret); // Strip off the domain name if (strchr(pszClientPrincipal, '@') != NULL) { strchr(pszClientPrincipal, '@')[0] = '\0'; } dwError = LwWc16snToMbs( pLogonName->pwszName, &pszLogonName, pLogonName->wAccountNameLen / 2); BAIL_ON_LW_ERROR(dwError); if (strcasecmp(pszClientPrincipal, pszLogonName)) { // The pac belongs to a different user dwError = LW_ERROR_INVALID_LOGIN_ID; BAIL_ON_LW_ERROR(dwError); } dwError = LwAllocateMemory( sLogonInfoLen, OUT_PPVOID(&pchLogonInfo)); BAIL_ON_LW_ERROR(dwError); memcpy(pchLogonInfo, pchLogonInfoStart, sLogonInfoLen); *ppchLogonInfo = pchLogonInfo; *psLogonInfo = sLogonInfoLen; cleanup: LW_SAFE_FREE_STRING(pszLogonName); LW_SAFE_FREE_MEMORY(pPacData); LW_SAFE_FREE_MEMORY(pchPacCopy); if (pszClientPrincipal != NULL) { krb5_free_unparsed_name(ctx, pszClientPrincipal); } return dwError; error: LW_SAFE_FREE_MEMORY(pchLogonInfo); *ppchLogonInfo = NULL; goto cleanup; }