static krb5_error_code decode_creds(krb5_context context, const void *data, size_t length, krb5_creds *creds) { krb5_error_code ret; krb5_storage *sp; sp = krb5_storage_from_readonly_mem(data, length); if (sp == NULL) return krb5_enomem(context); ret = krb5_ret_creds(sp, creds); krb5_storage_free(sp); if (ret) { krb5_set_error_message(context, ret, N_("Failed to read credential in scache", "")); return ret; } return 0; }
static krb5_error_code KRB5_CALLCONV xcc_get_next (krb5_context context, krb5_ccache id, krb5_cc_cursor *cursor, krb5_creds *creds) { struct xcc_cursor *c = *cursor; krb5_error_code ret; krb5_storage *sp; HeimCredRef cred; CFDataRef data; if (c->array == NULL) return KRB5_CC_END; next: if (c->offset >= CFArrayGetCount(c->array)) return KRB5_CC_END; cred = (HeimCredRef)CFArrayGetValueAtIndex(c->array, c->offset++); if (cred == NULL) return KRB5_CC_END; data = HeimCredCopyAttribute(cred, kHEIMAttrData); if (data == NULL) { goto next; } sp = krb5_storage_from_readonly_mem(CFDataGetBytePtr(data), CFDataGetLength(data)); if (sp == NULL) { CFRELEASE_NULL(data); return KRB5_CC_END; } ret = krb5_ret_creds(sp, creds); krb5_storage_free(sp); CFRELEASE_NULL(data); return ret; }
static krb5_error_code verify_logonname(krb5_context context, const struct PAC_INFO_BUFFER *logon_name, const krb5_data *data, time_t authtime, krb5_const_principal principal) { krb5_error_code ret; krb5_principal p2; uint32_t time1, time2; krb5_storage *sp; uint16_t len; char *s; sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, logon_name->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &time1), out); CHECK(ret, krb5_ret_uint32(sp, &time2), out); { uint64_t t1, t2; t1 = unix2nttime(authtime); t2 = ((uint64_t)time2 << 32) | time1; if (t1 != t2) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch"); return EINVAL; } } CHECK(ret, krb5_ret_uint16(sp, &len), out); if (len == 0) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC logon name length missing"); return EINVAL; } s = malloc(len); if (s == NULL) { krb5_storage_free(sp); return krb5_enomem(context); } ret = krb5_storage_read(sp, s, len); if (ret != len) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name"); return EINVAL; } krb5_storage_free(sp); { size_t ucs2len = len / 2; uint16_t *ucs2; size_t u8len; unsigned int flags = WIND_RW_LE; ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); if (ucs2 == NULL) return krb5_enomem(context); ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len); free(s); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); return ret; } ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string"); return ret; } u8len += 1; /* Add space for NUL */ s = malloc(u8len); if (s == NULL) { free(ucs2); return krb5_enomem(context); } ret = wind_ucs2utf8(ucs2, ucs2len, s, &u8len); free(ucs2); if (ret) { free(s); krb5_set_error_message(context, ret, "Failed to convert to UTF-8"); return ret; } } ret = krb5_parse_name_flags(context, s, KRB5_PRINCIPAL_PARSE_NO_REALM, &p2); free(s); if (ret) return ret; if (krb5_principal_compare_any_realm(context, principal, p2) != TRUE) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC logon name mismatch"); } krb5_free_principal(context, p2); return ret; out: return ret; }
KRB5_LIB_FUNCTION krb5_error_code KRB5_LIB_CALL krb5_pac_parse(krb5_context context, const void *ptr, size_t len, krb5_pac *pac) { krb5_error_code ret; krb5_pac p; krb5_storage *sp = NULL; uint32_t i, tmp, tmp2, header_end; p = calloc(1, sizeof(*p)); if (p == NULL) { ret = krb5_enomem(context); goto out; } sp = krb5_storage_from_readonly_mem(ptr, len); if (sp == NULL) { ret = krb5_enomem(context); goto out; } krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &tmp), out); CHECK(ret, krb5_ret_uint32(sp, &tmp2), out); if (tmp < 1) { ret = EINVAL; /* Too few buffers */ krb5_set_error_message(context, ret, N_("PAC have too few buffer", "")); goto out; } if (tmp2 != 0) { ret = EINVAL; /* Wrong version */ krb5_set_error_message(context, ret, N_("PAC have wrong version %d", ""), (int)tmp2); goto out; } p->pac = calloc(1, sizeof(*p->pac) + (sizeof(p->pac->buffers[0]) * (tmp - 1))); if (p->pac == NULL) { ret = krb5_enomem(context); goto out; } p->pac->numbuffers = tmp; p->pac->version = tmp2; header_end = PACTYPE_SIZE + (PAC_INFO_BUFFER_SIZE * p->pac->numbuffers); if (header_end > len) { ret = EINVAL; goto out; } for (i = 0; i < p->pac->numbuffers; i++) { CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].type), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].buffersize), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_lo), out); CHECK(ret, krb5_ret_uint32(sp, &p->pac->buffers[i].offset_hi), out); /* consistency checks */ if (p->pac->buffers[i].offset_lo & (PAC_ALIGNMENT - 1)) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC out of allignment", "")); goto out; } if (p->pac->buffers[i].offset_hi) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC high offset set", "")); goto out; } if (p->pac->buffers[i].offset_lo > len) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC offset off end", "")); goto out; } if (p->pac->buffers[i].offset_lo < header_end) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC offset inside header: %lu %lu", ""), (unsigned long)p->pac->buffers[i].offset_lo, (unsigned long)header_end); goto out; } if (p->pac->buffers[i].buffersize > len - p->pac->buffers[i].offset_lo){ ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC length off end", "")); goto out; } /* let save pointer to data we need later */ if (p->pac->buffers[i].type == PAC_SERVER_CHECKSUM) { if (p->server_checksum) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two server checksums", "")); goto out; } p->server_checksum = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_PRIVSVR_CHECKSUM) { if (p->privsvr_checksum) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two KDC checksums", "")); goto out; } p->privsvr_checksum = &p->pac->buffers[i]; } else if (p->pac->buffers[i].type == PAC_LOGON_NAME) { if (p->logon_name) { ret = EINVAL; krb5_set_error_message(context, ret, N_("PAC have two logon names", "")); goto out; } p->logon_name = &p->pac->buffers[i]; } } ret = krb5_data_copy(&p->data, ptr, len); if (ret) goto out; krb5_storage_free(sp); *pac = p; return 0; out: if (sp) krb5_storage_free(sp); if (p) { if (p->pac) free(p->pac); free(p); } *pac = NULL; return ret; }
static krb5_error_code verify_logonname(krb5_context context, const struct PAC_INFO_BUFFER *logon_name, const krb5_data *data, time_t authtime, krb5_const_principal principal) { krb5_error_code ret; uint32_t time1, time2; krb5_storage *sp; uint16_t len; char *s = NULL; char *principal_string = NULL; char *logon_string = NULL; sp = krb5_storage_from_readonly_mem((const char *)data->data + logon_name->offset_lo, logon_name->buffersize); if (sp == NULL) return krb5_enomem(context); krb5_storage_set_flags(sp, KRB5_STORAGE_BYTEORDER_LE); CHECK(ret, krb5_ret_uint32(sp, &time1), out); CHECK(ret, krb5_ret_uint32(sp, &time2), out); { uint64_t t1, t2; t1 = unix2nttime(authtime); t2 = ((uint64_t)time2 << 32) | time1; /* * When neither the ticket nor the PAC set an explicit authtime, * both times are zero, but relative to different time scales. * So we must compare "not set" values without converting to a * common time reference. */ if (t1 != t2 && (t2 != 0 && authtime != 0)) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC timestamp mismatch"); return EINVAL; } } CHECK(ret, krb5_ret_uint16(sp, &len), out); if (len == 0) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "PAC logon name length missing"); return EINVAL; } s = malloc(len); if (s == NULL) { krb5_storage_free(sp); return krb5_enomem(context); } ret = krb5_storage_read(sp, s, len); if (ret != len) { krb5_storage_free(sp); krb5_set_error_message(context, EINVAL, "Failed to read PAC logon name"); return EINVAL; } krb5_storage_free(sp); { size_t ucs2len = len / 2; uint16_t *ucs2; size_t u8len; unsigned int flags = WIND_RW_LE; ucs2 = malloc(sizeof(ucs2[0]) * ucs2len); if (ucs2 == NULL) return krb5_enomem(context); ret = wind_ucs2read(s, len, &flags, ucs2, &ucs2len); free(s); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to convert string to UCS-2"); return ret; } ret = wind_ucs2utf8_length(ucs2, ucs2len, &u8len); if (ret) { free(ucs2); krb5_set_error_message(context, ret, "Failed to count length of UCS-2 string"); return ret; } u8len += 1; /* Add space for NUL */ logon_string = malloc(u8len); if (logon_string == NULL) { free(ucs2); return krb5_enomem(context); } ret = wind_ucs2utf8(ucs2, ucs2len, logon_string, &u8len); free(ucs2); if (ret) { free(logon_string); krb5_set_error_message(context, ret, "Failed to convert to UTF-8"); return ret; } } ret = krb5_unparse_name_flags(context, principal, KRB5_PRINCIPAL_UNPARSE_NO_REALM | KRB5_PRINCIPAL_UNPARSE_DISPLAY, &principal_string); if (ret) { free(logon_string); return ret; } ret = strcmp(logon_string, principal_string); if (ret != 0) { ret = EINVAL; krb5_set_error_message(context, ret, "PAC logon name [%s] mismatch principal name [%s]", logon_string, principal_string); } free(logon_string); free(principal_string); return ret; out: return ret; }