summaryrefslogtreecommitdiffstats
path: root/src/lib/krb5/krb/pac.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/krb5/krb/pac.c')
-rw-r--r--src/lib/krb5/krb/pac.c730
1 files changed, 700 insertions, 30 deletions
diff --git a/src/lib/krb5/krb/pac.c b/src/lib/krb5/krb/pac.c
index c5a7065624..297e895501 100644
--- a/src/lib/krb5/krb/pac.c
+++ b/src/lib/krb5/krb/pac.c
@@ -8,7 +8,7 @@
* require a specific license from the United States Government.
* It is the responsibility of any person or organization contemplating
* export to obtain such a license before exporting.
- *
+ *
* WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
* distribute this software and its documentation for any purpose and
* without fee is hereby granted, provided that the above copyright
@@ -27,6 +27,7 @@
#include "k5-int.h"
#include "k5-utf8.h"
+#include "authdata.h"
/* draft-brezak-win2k-krb-authz-00 */
@@ -46,9 +47,12 @@ typedef struct _PAC_INFO_BUFFER {
/* ulType */
#define PAC_LOGON_INFO 1
+#define PAC_CREDENTIALS_INFO 2
#define PAC_SERVER_CHECKSUM 6
#define PAC_PRIVSVR_CHECKSUM 7
#define PAC_CLIENT_INFO 10
+#define PAC_DELEGATION_INFO 11
+#define PAC_UPN_DNS_INFO 12
typedef struct _PACTYPE {
krb5_ui_4 cBuffers;
@@ -66,6 +70,7 @@ typedef struct _PACTYPE {
struct krb5_pac_data {
PACTYPE *pac; /* PAC header + info buffer array */
krb5_data data; /* PAC data (including uninitialised header) */
+ krb5_boolean verified;
};
static krb5_error_code
@@ -93,7 +98,7 @@ k5_pac_add_buffer(krb5_context context,
/* Check there isn't already a buffer of this type */
if (k5_pac_locate_buffer(context, pac, type, NULL) == 0) {
- return EINVAL;
+ return EEXIST;
}
header = (PACTYPE *)realloc(pac->pac,
@@ -148,6 +153,8 @@ k5_pac_add_buffer(krb5_context context,
out_data->length = data->length;
}
+ pac->verified = FALSE;
+
return 0;
}
@@ -228,7 +235,7 @@ krb5_pac_get_buffer(krb5_context context,
ret = k5_pac_locate_buffer(context, pac, type, &d);
if (ret != 0)
return ret;
-
+
data->data = malloc(d.length);
if (data->data == NULL)
return ENOMEM;
@@ -277,7 +284,7 @@ krb5_pac_init(krb5_context context,
pac->pac = (PACTYPE *)malloc(sizeof(PACTYPE));
if (pac->pac == NULL) {
- free( pac);
+ free(pac);
return ENOMEM;
}
@@ -291,11 +298,54 @@ krb5_pac_init(krb5_context context,
return ENOMEM;
}
+ pac->verified = FALSE;
+
*ppac = pac;
return 0;
}
+static krb5_error_code
+k5_pac_copy(krb5_context context,
+ krb5_pac src,
+ krb5_pac *dst)
+{
+ size_t header_len;
+ krb5_ui_4 cbuffers;
+ krb5_error_code code;
+ krb5_pac pac;
+
+ cbuffers = src->pac->cBuffers;
+ if (cbuffers != 0)
+ cbuffers--;
+
+ header_len = sizeof(PACTYPE) + cbuffers * sizeof(PAC_INFO_BUFFER);
+
+ pac = (krb5_pac)malloc(sizeof(*pac));
+ if (pac == NULL)
+ return ENOMEM;
+
+ pac->pac = (PACTYPE *)malloc(header_len);
+ if (pac->pac == NULL) {
+ free(pac);
+ return ENOMEM;
+ }
+
+ memcpy(pac->pac, src->pac, header_len);
+
+ code = krb5int_copy_data_contents(context, &src->data, &pac->data);
+ if (code != 0) {
+ free(pac->pac);
+ free(pac);
+ return ENOMEM;
+ }
+
+ pac->verified = src->verified;
+ *dst = pac;
+
+ return 0;
+}
+
/*
* Parse the supplied data into the PAC allocated by this function
*/
@@ -379,7 +429,8 @@ krb5_pac_parse(krb5_context context,
}
static krb5_error_code
-k5_time_to_seconds_since_1970(krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
+k5_time_to_seconds_since_1970(krb5_int64 ntTime,
+ krb5_timestamp *elapsedSeconds)
{
krb5_ui_8 abstime;
@@ -393,10 +444,11 @@ k5_time_to_seconds_since_1970(krb5_int64 ntTime, krb5_timestamp *elapsedSeconds)
*elapsedSeconds = abstime;
return 0;
-}
+}
static krb5_error_code
-k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
+k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds,
+ krb5_ui_8 *ntTime)
{
*ntTime = elapsedSeconds;
@@ -404,7 +456,7 @@ k5_seconds_since_1970_to_time(krb5_timestamp elapsedSeconds, krb5_ui_8 *ntTime)
*ntTime += NT_TIME_EPOCH;
*ntTime *= 10000000;
-
+
return 0;
}
@@ -441,10 +493,11 @@ k5_pac_validate_client(krb5_context context,
return ret;
if (client_info.length < PAC_CLIENT_INFO_LENGTH + pac_princname_length ||
- pac_princname_length % 2)
+ pac_princname_length % 2)
return ERANGE;
- ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2, &pac_princname, NULL);
+ ret = krb5int_ucs2lecs_to_utf8s(p, (size_t)pac_princname_length / 2,
+ &pac_princname, NULL);
if (ret != 0)
return ret;
@@ -457,7 +510,10 @@ k5_pac_validate_client(krb5_context context,
free(pac_princname);
if (pac_authtime != authtime ||
- krb5_principal_compare(context, pac_principal, principal) == FALSE)
+ !krb5_principal_compare_flags(context,
+ pac_principal,
+ principal,
+ KRB5_PRINCIPAL_COMPARE_IGNORE_REALM))
ret = KRB5KRB_AP_WRONG_PRINC;
krb5_free_principal(context, pac_principal);
@@ -513,7 +569,8 @@ k5_pac_verify_server_checksum(krb5_context context,
krb5_boolean valid;
krb5_octet *p;
- ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &checksum_data);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_SERVER_CHECKSUM, &checksum_data);
if (ret != 0)
return ret;
@@ -533,19 +590,22 @@ k5_pac_verify_server_checksum(krb5_context context,
memcpy(pac_data.data, pac->data.data, pac->data.length);
/* Zero out both checksum buffers */
- ret = k5_pac_zero_signature(context, pac, PAC_SERVER_CHECKSUM, &pac_data);
+ ret = k5_pac_zero_signature(context, pac,
+ PAC_SERVER_CHECKSUM, &pac_data);
if (ret != 0) {
free(pac_data.data);
return ret;
}
- ret = k5_pac_zero_signature(context, pac, PAC_PRIVSVR_CHECKSUM, &pac_data);
+ ret = k5_pac_zero_signature(context, pac,
+ PAC_PRIVSVR_CHECKSUM, &pac_data);
if (ret != 0) {
free(pac_data.data);
return ret;
}
- ret = krb5_c_verify_checksum(context, server, KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ ret = krb5_c_verify_checksum(context, server,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM,
&pac_data, &checksum, &valid);
free(pac_data.data);
@@ -571,14 +631,16 @@ k5_pac_verify_kdc_checksum(krb5_context context,
krb5_boolean valid;
krb5_octet *p;
- ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_PRIVSVR_CHECKSUM, &privsvr_checksum);
if (ret != 0)
return ret;
if (privsvr_checksum.length < PAC_SIGNATURE_DATA_LENGTH)
return KRB5_BAD_MSIZE;
- ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_checksum);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_SERVER_CHECKSUM, &server_checksum);
if (ret != 0)
return ret;
@@ -593,7 +655,8 @@ k5_pac_verify_kdc_checksum(krb5_context context,
server_checksum.data += PAC_SIGNATURE_DATA_LENGTH;
server_checksum.length -= PAC_SIGNATURE_DATA_LENGTH;
- ret = krb5_c_verify_checksum(context, privsvr, KRB5_KEYUSAGE_APP_DATA_CKSUM,
+ ret = krb5_c_verify_checksum(context, privsvr,
+ KRB5_KEYUSAGE_APP_DATA_CKSUM,
&server_checksum, &checksum, &valid);
if (ret != 0)
return ret;
@@ -633,6 +696,8 @@ krb5_pac_verify(krb5_context context,
return ret;
}
+ pac->verified = TRUE;
+
return 0;
}
@@ -650,12 +715,14 @@ k5_insert_client_info(krb5_context context,
krb5_ui_8 nt_authtime;
/* If we already have a CLIENT_INFO buffer, then just validate it */
- if (k5_pac_locate_buffer(context, pac, PAC_CLIENT_INFO, &client_info) == 0) {
+ if (k5_pac_locate_buffer(context, pac,
+ PAC_CLIENT_INFO, &client_info) == 0) {
return k5_pac_validate_client(context, pac, authtime, principal);
}
ret = krb5_unparse_name_flags(context, principal,
- KRB5_PRINCIPAL_UNPARSE_NO_REALM, &princ_name_utf8);
+ KRB5_PRINCIPAL_UNPARSE_NO_REALM,
+ &princ_name_utf8);
if (ret != 0)
goto cleanup;
@@ -668,7 +735,8 @@ k5_insert_client_info(krb5_context context,
client_info.length = PAC_CLIENT_INFO_LENGTH + princ_name_ucs2_len;
client_info.data = NULL;
- ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO, &client_info, TRUE, &client_info);
+ ret = k5_pac_add_buffer(context, pac, PAC_CLIENT_INFO,
+ &client_info, TRUE, &client_info);
if (ret != 0)
goto cleanup;
@@ -685,12 +753,11 @@ k5_insert_client_info(krb5_context context,
/* copy in principal name */
memcpy(p, princ_name_ucs2, princ_name_ucs2_len);
-
+
cleanup:
- if (princ_name_utf8 != NULL)
- free(princ_name_utf8);
if (princ_name_ucs2 != NULL)
free(princ_name_ucs2);
+ krb5_free_unparsed_name(context, princ_name_utf8);
return ret;
}
@@ -716,7 +783,10 @@ k5_insert_checksum(krb5_context context,
ret = k5_pac_locate_buffer(context, pac, type, &cksumdata);
if (ret == 0) {
- /* If we're resigning PAC, make sure we can fit checksum into existing buffer */
+ /*
+ * If we're resigning PAC, make sure we can fit checksum
+ * into existing buffer
+ */
if (cksumdata.length != PAC_SIGNATURE_DATA_LENGTH + len)
return ERANGE;
@@ -726,7 +796,9 @@ k5_insert_checksum(krb5_context context,
cksumdata.length = PAC_SIGNATURE_DATA_LENGTH + len;
cksumdata.data = NULL;
- ret = k5_pac_add_buffer(context, pac, type, &cksumdata, TRUE, &cksumdata);
+ ret = k5_pac_add_buffer(context, pac,
+ type, &cksumdata,
+ TRUE, &cksumdata);
if (ret != 0)
return ret;
}
@@ -745,7 +817,8 @@ k5_pac_encode_header(krb5_context context, krb5_pac pac)
unsigned char *p;
size_t header_len;
- header_len = PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
+ header_len = PACTYPE_LENGTH +
+ (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH);
assert(pac->data.length >= header_len);
p = (unsigned char *)pac->data.data;
@@ -818,7 +891,8 @@ krb5int_pac_sign(krb5_context context,
return ret;
/* Generate the server checksum over the entire PAC */
- ret = k5_pac_locate_buffer(context, pac, PAC_SERVER_CHECKSUM, &server_cksum);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_SERVER_CHECKSUM, &server_cksum);
if (ret != 0)
return ret;
@@ -838,7 +912,8 @@ krb5int_pac_sign(krb5_context context,
return ret;
/* Generate the privsvr checksum over the server checksum buffer */
- ret = k5_pac_locate_buffer(context, pac, PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
+ ret = k5_pac_locate_buffer(context, pac,
+ PAC_PRIVSVR_CHECKSUM, &privsvr_cksum);
if (ret != 0)
return ret;
@@ -865,8 +940,603 @@ krb5int_pac_sign(krb5_context context,
data->length = pac->data.length;
memcpy(data->data, pac->data.data, pac->data.length);
- memset(pac->data.data, 0, PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
+ memset(pac->data.data, 0,
+ PACTYPE_LENGTH + (pac->pac->cBuffers * PAC_INFO_BUFFER_LENGTH));
+
+ return 0;
+}
+
+/*
+ * PAC auth data attribute backend
+ */
+struct mspac_context {
+ krb5_pac pac;
+};
+
+static krb5_error_code
+mspac_init(krb5_context kcontext, void **plugin_context)
+{
+ *plugin_context = NULL;
+ return 0;
+}
+
+static void
+mspac_flags(krb5_context kcontext,
+ void *plugin_context,
+ krb5_authdatatype ad_type,
+ krb5_flags *flags)
+{
+ *flags = AD_USAGE_KDC_ISSUED;
+}
+
+static void
+mspac_fini(krb5_context kcontext, void *plugin_context)
+{
+ return;
+}
+
+static krb5_error_code
+mspac_request_init(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void **request_context)
+{
+ struct mspac_context *pacctx;
+
+ pacctx = (struct mspac_context *)malloc(sizeof(*pacctx));
+ if (pacctx == NULL)
+ return ENOMEM;
+
+ pacctx->pac = NULL;
+
+ *request_context = pacctx;
+
+ return 0;
+}
+
+static krb5_error_code
+mspac_import_authdata(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_authdata **authdata,
+ krb5_boolean kdc_issued,
+ krb5_const_principal kdc_issuer)
+{
+ krb5_error_code code;
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+
+ if (kdc_issued)
+ return EINVAL;
+
+ if (pacctx->pac != NULL) {
+ krb5_pac_free(kcontext, pacctx->pac);
+ pacctx->pac = NULL;
+ }
+
+ assert(authdata[0] != NULL);
+ assert((authdata[0]->ad_type & AD_TYPE_FIELD_TYPE_MASK) ==
+ KRB5_AUTHDATA_WIN2K_PAC);
+
+ code = krb5_pac_parse(kcontext, authdata[0]->contents,
+ authdata[0]->length, &pacctx->pac);
+
+ return code;
+}
+
+static krb5_error_code
+mspac_export_authdata(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_flags usage,
+ krb5_authdata ***out_authdata)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_authdata **authdata;
+ krb5_data data;
+
+ if (pacctx->pac == NULL)
+ return 0;
+
+ authdata = calloc(2, sizeof(krb5_authdata *));
+ if (authdata == NULL)
+ return ENOMEM;
+
+ authdata[0] = calloc(1, sizeof(krb5_authdata));
+ if (authdata[0] == NULL) {
+ free(authdata);
+ return ENOMEM;
+ }
+ authdata[1] = NULL;
+
+ code = krb5int_copy_data_contents(kcontext, &pacctx->pac->data, &data);
+ if (code != 0) {
+ krb5_free_authdata(kcontext, authdata);
+ return code;
+ }
+
+ authdata[0]->magic = KV5M_AUTHDATA;
+ authdata[0]->ad_type = KRB5_AUTHDATA_WIN2K_PAC;
+ authdata[0]->length = data.length;
+ authdata[0]->contents = (krb5_octet *)data.data;
+
+ authdata[1] = NULL;
+
+ *out_authdata = authdata;
+
+ return 0;
+}
+
+static krb5_error_code
+mspac_verify(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ const krb5_auth_context *auth_context,
+ const krb5_keyblock *key,
+ const krb5_ap_req *req)
+{
+ krb5_error_code code;
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+
+ if (pacctx->pac == NULL)
+ return EINVAL;
+
+ code = krb5_pac_verify(kcontext,
+ pacctx->pac,
+ req->ticket->enc_part2->times.authtime,
+ req->ticket->enc_part2->client,
+ key,
+ NULL);
+
+#if 0
+ /*
+ * Now, we could return 0 and just set pac->verified to FALSE.
+ * Thoughts?
+ */
+ if (code == KRB5KRB_AP_ERR_BAD_INTEGRITY) {
+ assert(pacctx->pac->verified == FALSE);
+ code = 0;
+ }
+#endif
+
+ return code;
+}
+
+static void
+mspac_request_fini(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+
+ if (pacctx != NULL) {
+ if (pacctx->pac != NULL)
+ krb5_pac_free(kcontext, pacctx->pac);
+
+ free(pacctx);
+ }
+}
+
+#define STRLENOF(x) (sizeof((x)) - 1)
+
+static struct {
+ krb5_ui_4 type;
+ krb5_data attribute;
+} mspac_attribute_types[] = {
+ { (krb5_ui_4)-1, { KV5M_DATA, STRLENOF("mspac:"), "mspac:" } },
+ { PAC_LOGON_INFO, { KV5M_DATA, STRLENOF("mspac:logon-info"), "mspac:logon-info" } },
+ { PAC_CREDENTIALS_INFO, { KV5M_DATA, STRLENOF("mspac:credentials-info"), "mspac:credentials-info" } },
+ { PAC_SERVER_CHECKSUM, { KV5M_DATA, STRLENOF("mspac:server-checksum"), "mspac:server-checksum" } },
+ { PAC_PRIVSVR_CHECKSUM, { KV5M_DATA, STRLENOF("mspac:privsvr-checksum"), "mspac:privsvr-checksum" } },
+ { PAC_CLIENT_INFO, { KV5M_DATA, STRLENOF("mspac:client-info"), "mspac:client-info" } },
+ { PAC_DELEGATION_INFO, { KV5M_DATA, STRLENOF("mspac:delegation-info"), "mspac:delegation-info" } },
+ { PAC_UPN_DNS_INFO, { KV5M_DATA, STRLENOF("mspac:upn-dns-info"), "mspac:upn-dns-info" } },
+};
+
+#define MSPAC_ATTRIBUTE_COUNT (sizeof(mspac_attribute_types)/sizeof(mspac_attribute_types[0]))
+
+static krb5_error_code
+mspac_type2attr(krb5_ui_4 type, krb5_data *attr)
+{
+ unsigned int i;
+
+ for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
+ if (mspac_attribute_types[i].type == type) {
+ *attr = mspac_attribute_types[i].attribute;
+ return 0;
+ }
+ }
+
+ return ENOENT;
+}
+
+static krb5_error_code
+mspac_attr2type(const krb5_data *attr, krb5_ui_4 *type)
+{
+ unsigned int i;
+
+ for (i = 0; i < MSPAC_ATTRIBUTE_COUNT; i++) {
+ if (attr->length == mspac_attribute_types[i].attribute.length &&
+ strncasecmp(attr->data, mspac_attribute_types[i].attribute.data, attr->length) == 0) {
+ *type = mspac_attribute_types[i].type;
+ return 0;
+ }
+ }
+
+ if (attr->length > STRLENOF("mspac:") &&
+ strncasecmp(attr->data, "mspac:", STRLENOF("mspac:")) == 0)
+ {
+ char *p = &attr->data[STRLENOF("mspac:")];
+ char *endptr;
+
+ *type = strtoul(p, &endptr, 10);
+ if (*type != 0 && *endptr == '\0')
+ return 0;
+ }
+
+ return ENOENT;
+}
+
+static krb5_error_code
+mspac_get_attribute_types(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_data **out_attrs)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ unsigned int i, j;
+ krb5_data *attrs;
+ krb5_error_code code;
+
+ if (pacctx->pac == NULL)
+ return ENOENT;
+
+ attrs = calloc(1 + pacctx->pac->pac->cBuffers + 1, sizeof(krb5_data));
+ if (attrs == NULL)
+ return ENOMEM;
+
+ j = 0;
+
+ /* The entire PAC */
+ code = krb5int_copy_data_contents(kcontext,
+ &mspac_attribute_types[0].attribute,
+ &attrs[j++]);
+ if (code != 0) {
+ free(attrs);
+ return code;
+ }
+
+ /* PAC buffers */
+ for (i = 0; i < pacctx->pac->pac->cBuffers; i++) {
+ krb5_data attr;
+
+ code = mspac_type2attr(pacctx->pac->pac->Buffers[i].ulType, &attr);
+ if (code == 0) {
+ code = krb5int_copy_data_contents(kcontext, &attr, &attrs[j++]);
+ if (code != 0) {
+ krb5int_free_data_list(kcontext, attrs);
+ return code;
+ }
+ } else {
+ int length;
+
+ length = asprintf(&attrs[j].data, "mspac:%d",
+ pacctx->pac->pac->Buffers[i].ulType);
+ if (length < 0) {
+ krb5int_free_data_list(kcontext, attrs);
+ return ENOMEM;
+ }
+ attrs[j++].length = length;
+ }
+ }
+ attrs[j].data = NULL;
+ attrs[j].length = 0;
+
+ *out_attrs = attrs;
return 0;
}
+static krb5_error_code
+mspac_get_attribute(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ const krb5_data *attribute,
+ krb5_boolean *authenticated,
+ krb5_boolean *complete,
+ krb5_data *value,
+ krb5_data *display_value,
+ int *more)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_ui_4 type;
+
+ value->data = NULL;
+ value->length = 0;
+
+ if (display_value != NULL) {
+ display_value->data = NULL;
+ display_value->length = 0;
+ }
+
+ if (*more != -1 || pacctx->pac == NULL)
+ return ENOENT;
+
+ code = mspac_attr2type(attribute, &type);
+ if (code != 0)
+ return code;
+
+ /* -1 is a magic type that refers to the entire PAC */
+ if (type == (krb5_ui_4)-1) {
+ if (value != NULL)
+ code = krb5int_copy_data_contents(kcontext,
+ &pacctx->pac->data,
+ value);
+ else
+ code = 0;
+ } else {
+ if (value != NULL)
+ code = krb5_pac_get_buffer(kcontext, pacctx->pac, type, value);
+ else
+ code = k5_pac_locate_buffer(kcontext, pacctx->pac, type, NULL);
+ }
+ if (code == 0) {
+ *authenticated = pacctx->pac->verified;
+ *complete = TRUE;
+ }
+
+ *more = 0;
+
+ return code;
+}
+
+static krb5_error_code
+mspac_set_attribute(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_boolean complete,
+ const krb5_data *attribute,
+ const krb5_data *value)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_ui_4 type;
+
+ if (pacctx->pac == NULL)
+ return ENOENT;
+
+ code = mspac_attr2type(attribute, &type);
+ if (code != 0)
+ return code;
+
+ /* -1 is a magic type that refers to the entire PAC */
+ if (type == (krb5_ui_4)-1) {
+ krb5_pac newpac;
+
+ code = krb5_pac_parse(kcontext, value->data, value->length, &newpac);
+ if (code != 0)
+ return code;
+
+ krb5_pac_free(kcontext, pacctx->pac);
+ pacctx->pac = newpac;
+ } else {
+ code = krb5_pac_add_buffer(kcontext, pacctx->pac, type, value);
+ }
+
+ return code;
+}
+
+static krb5_error_code
+mspac_export_internal(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_boolean restrict_authenticated,
+ void **ptr)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_pac pac;
+
+ *ptr = NULL;
+
+ if (pacctx->pac == NULL)
+ return 0;
+
+ if (restrict_authenticated && (pacctx->pac->verified) == FALSE)
+ return 0;
+
+ code = krb5_pac_parse(kcontext, pacctx->pac->data.data,
+ pacctx->pac->data.length, &pac);
+ if (code == 0) {
+ pac->verified = pacctx->pac->verified;
+ *ptr = pac;
+ }
+
+ return code;
+}
+
+static void
+mspac_free_internal(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ void *ptr)
+{
+ if (ptr != NULL)
+ krb5_pac_free(kcontext, (krb5_pac)ptr);
+
+ return;
+}
+
+static krb5_error_code
+mspac_size(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ size_t *sizep)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+
+ *sizep += sizeof(krb5_int32);
+
+ if (pacctx->pac != NULL)
+ *sizep += pacctx->pac->data.length;
+
+ *sizep += sizeof(krb5_int32);
+
+ return 0;
+}
+
+static krb5_error_code
+mspac_externalize(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_octet **buffer,
+ size_t *lenremain)
+{
+ krb5_error_code code = 0;
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ size_t required = 0;
+ krb5_octet *bp;
+ size_t remain;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ if (pacctx->pac != NULL) {
+ mspac_size(kcontext, context, plugin_context,
+ request_context, &required);
+
+ if (required <= remain) {
+ krb5_ser_pack_int32((krb5_int32)pacctx->pac->data.length,
+ &bp, &remain);
+ krb5_ser_pack_bytes((krb5_octet *)pacctx->pac->data.data,
+ (size_t)pacctx->pac->data.length,
+ &bp, &remain);
+ krb5_ser_pack_int32((krb5_int32)pacctx->pac->verified,
+ &bp, &remain);
+ } else {
+ code = ENOMEM;
+ }
+ } else {
+ krb5_ser_pack_int32(0, &bp, &remain); /* length */
+ krb5_ser_pack_int32(0, &bp, &remain); /* verified */
+ }
+
+ *buffer = bp;
+ *lenremain = remain;
+
+ return code;
+}
+
+static krb5_error_code
+mspac_internalize(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ krb5_octet **buffer,
+ size_t *lenremain)
+{
+ struct mspac_context *pacctx = (struct mspac_context *)request_context;
+ krb5_error_code code;
+ krb5_int32 ibuf;
+ krb5_octet *bp;
+ size_t remain;
+ krb5_pac pac = NULL;
+
+ bp = *buffer;
+ remain = *lenremain;
+
+ /* length */
+ code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (code != 0)
+ return code;
+
+ if (ibuf != 0) {
+ code = krb5_pac_parse(kcontext, bp, ibuf, &pac);
+ if (code != 0)
+ return code;
+
+ bp += ibuf;
+ remain -= ibuf;
+ }
+
+ /* verified */
+ code = krb5_ser_unpack_int32(&ibuf, &bp, &remain);
+ if (code != 0) {
+ krb5_pac_free(kcontext, pac);
+ return code;
+ }
+
+ if (pac != NULL) {
+ pac->verified = (ibuf != 0);
+ }
+
+ if (pacctx->pac != NULL) {
+ krb5_pac_free(kcontext, pacctx->pac);
+ }
+
+ pacctx->pac = pac;
+
+ *buffer = bp;
+ *lenremain = remain;
+
+ return 0;
+}
+
+static krb5_error_code
+mspac_copy(krb5_context kcontext,
+ krb5_authdata_context context,
+ void *plugin_context,
+ void *request_context,
+ void *dst_plugin_context,
+ void *dst_request_context)
+{
+ struct mspac_context *srcctx = (struct mspac_context *)request_context;
+ struct mspac_context *dstctx = (struct mspac_context *)dst_request_context;
+ krb5_error_code code = 0;
+
+ assert(dstctx != NULL);
+ assert(dstctx->pac == NULL);
+
+ if (srcctx->pac != NULL)
+ code = k5_pac_copy(kcontext, srcctx->pac, &dstctx->pac);
+
+ return code;
+}
+
+static krb5_authdatatype mspac_ad_types[] = { KRB5_AUTHDATA_WIN2K_PAC, 0 };
+
+krb5plugin_authdata_client_ftable_v0 krb5int_mspac_authdata_client_ftable = {
+ "mspac",
+ mspac_ad_types,
+ mspac_init,
+ mspac_fini,
+ mspac_flags,
+ mspac_request_init,
+ mspac_request_fini,
+ mspac_get_attribute_types,
+ mspac_get_attribute,
+ mspac_set_attribute,
+ NULL, /* delete_attribute_proc */
+ mspac_export_authdata,
+ mspac_import_authdata,
+ mspac_export_internal,
+ mspac_free_internal,
+ mspac_verify,
+ mspac_size,
+ mspac_externalize,
+ mspac_internalize,
+ mspac_copy
+};
+