/* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */ /* lib/krb5/krb/ser_ctx.c - Serialize krb5_context structure */ /* * Copyright 1995, 2007, 2008 by the Massachusetts Institute of Technology. * All Rights Reserved. * * Export of this software from the United States of America may * 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 * notice appear in all copies and that both that copyright notice and * this permission notice appear in supporting documentation, and that * the name of M.I.T. not be used in advertising or publicity pertaining * to distribution of the software without specific, written prior * permission. Furthermore if you modify this software you must label * your software as modified software and not distribute it in such a * fashion that it might be confused with the original M.I.T. software. * M.I.T. makes no representations about the suitability of * this software for any purpose. It is provided "as is" without express * or implied warranty. */ #include "k5-int.h" #include "int-proto.h" /* * Routines to deal with externalizing the krb5_context: * krb5_context_size(); * krb5_context_externalize(); * krb5_context_internalize(); * * Routines to deal with externalizing the krb5_os_context: * krb5_oscontext_size(); * krb5_oscontext_externalize(); * krb5_oscontext_internalize(); * * Routines to deal with externalizing the profile. * profile_ser_size(); * profile_ser_externalize(); * profile_ser_internalize(); * * Interface to initialize serializing of krb5_context and krb5_os_context: * krb5_ser_context_init(); */ static krb5_error_code krb5_context_size(krb5_context, krb5_pointer, size_t *); static krb5_error_code krb5_context_externalize(krb5_context, krb5_pointer, krb5_octet **, size_t *); static krb5_error_code krb5_context_internalize(krb5_context, krb5_pointer *, krb5_octet **, size_t *); static krb5_error_code krb5_oscontext_size(krb5_context, krb5_pointer, size_t *); static krb5_error_code krb5_oscontext_externalize(krb5_context, krb5_pointer, krb5_octet **, size_t *); static krb5_error_code krb5_oscontext_internalize(krb5_context, krb5_pointer *, krb5_octet **, size_t *); #ifndef LEAN_CLIENT krb5_error_code profile_ser_size(krb5_context, krb5_pointer, size_t *); krb5_error_code profile_ser_externalize(krb5_context, krb5_pointer, krb5_octet **, size_t *); krb5_error_code profile_ser_internalize(krb5_context, krb5_pointer *, krb5_octet **, size_t *); #endif /* LEAN_CLIENT */ /* Local data */ static const krb5_ser_entry krb5_context_ser_entry = { KV5M_CONTEXT, /* Type */ krb5_context_size, /* Sizer routine */ krb5_context_externalize, /* Externalize routine */ krb5_context_internalize /* Internalize routine */ }; static const krb5_ser_entry krb5_oscontext_ser_entry = { KV5M_OS_CONTEXT, /* Type */ krb5_oscontext_size, /* Sizer routine */ krb5_oscontext_externalize, /* Externalize routine */ krb5_oscontext_internalize /* Internalize routine */ }; #ifndef LEAN_CLIENT static const krb5_ser_entry krb5_profile_ser_entry = { PROF_MAGIC_PROFILE, /* Type */ profile_ser_size, /* Sizer routine */ profile_ser_externalize, /* Externalize routine */ profile_ser_internalize /* Internalize routine */ }; #endif /* LEAN_CLIENT */ static inline unsigned int etypes_len(krb5_enctype *list) { return (list == NULL) ? 0 : k5_count_etypes(list); } /* * krb5_context_size() - Determine the size required to externalize the * krb5_context. */ static krb5_error_code krb5_context_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep) { krb5_error_code kret; size_t required; krb5_context context; /* * The KRB5 context itself requires: * krb5_int32 for KV5M_CONTEXT * krb5_int32 for sizeof(default_realm) * strlen(default_realm) for default_realm. * krb5_int32 for n_in_tkt_etypes*sizeof(krb5_int32) * nktypes*sizeof(krb5_int32) for in_tkt_etypes. * krb5_int32 for n_tgs_etypes*sizeof(krb5_int32) * nktypes*sizeof(krb5_int32) for tgs_etypes. * krb5_int32 for clockskew * krb5_int32 for kdc_req_sumtype * krb5_int32 for ap_req_sumtype * krb5_int32 for safe_sumtype * krb5_int32 for kdc_default_options * krb5_int32 for library_options * krb5_int32 for profile_secure * krb5_int32 for fcc_default_format * <> for os_context * <> for db_context * <> for profile * krb5_int32 for trailer. */ kret = EINVAL; if ((context = (krb5_context) arg)) { /* Calculate base length */ required = (14 * sizeof(krb5_int32) + (etypes_len(context->in_tkt_etypes) * sizeof(krb5_int32)) + (etypes_len(context->tgs_etypes) * sizeof(krb5_int32))); if (context->default_realm) required += strlen(context->default_realm); /* Calculate size required by os_context, if appropriate */ kret = krb5_size_opaque(kcontext, KV5M_OS_CONTEXT, (krb5_pointer) &context->os_context, &required); /* Calculate size required by db_context, if appropriate */ if (!kret && context->dal_handle) kret = krb5_size_opaque(kcontext, KV5M_DB_CONTEXT, (krb5_pointer) context->dal_handle, &required); /* Finally, calculate size required by profile, if appropriate */ if (!kret && context->profile) kret = krb5_size_opaque(kcontext, PROF_MAGIC_PROFILE, (krb5_pointer) context->profile, &required); } if (!kret) *sizep += required; return(kret); } /* * krb5_context_externalize() - Externalize the krb5_context. */ static krb5_error_code krb5_context_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain) { krb5_error_code kret; krb5_context context; size_t required; krb5_octet *bp; size_t remain; unsigned int i; required = 0; bp = *buffer; remain = *lenremain; context = (krb5_context) arg; if (!context) return (EINVAL); if (context->magic != KV5M_CONTEXT) return (KV5M_CONTEXT); if ((kret = krb5_context_size(kcontext, arg, &required))) return (kret); if (required > remain) return (ENOMEM); /* First write our magic number */ kret = krb5_ser_pack_int32(KV5M_CONTEXT, &bp, &remain); if (kret) return (kret); /* Now sizeof default realm */ kret = krb5_ser_pack_int32((context->default_realm) ? (krb5_int32) strlen(context->default_realm) : 0, &bp, &remain); if (kret) return (kret); /* Now default_realm bytes */ if (context->default_realm) { kret = krb5_ser_pack_bytes((krb5_octet *) context->default_realm, strlen(context->default_realm), &bp, &remain); if (kret) return (kret); } /* Now number of initial ticket ktypes */ kret = krb5_ser_pack_int32(etypes_len(context->in_tkt_etypes), &bp, &remain); if (kret) return (kret); /* Now serialize ktypes */ if (context->in_tkt_etypes) { for (i = 0; context->in_tkt_etypes[i]; i++) { kret = krb5_ser_pack_int32(context->in_tkt_etypes[i], &bp, &remain); if (kret) return (kret); } } /* Now number of default ktypes */ kret = krb5_ser_pack_int32(etypes_len(context->tgs_etypes), &bp, &remain); if (kret) return (kret); /* Now serialize ktypes */ if (context->tgs_etypes) { for (i = 0; context->tgs_etypes[i]; i++) { kret = krb5_ser_pack_int32(context->tgs_etypes[i], &bp, &remain); if (kret) return (kret); } } /* Now allowable clockskew */ kret = krb5_ser_pack_int32((krb5_int32) context->clockskew, &bp, &remain); if (kret) return (kret); /* Now kdc_req_sumtype */ kret = krb5_ser_pack_int32((krb5_int32) context->kdc_req_sumtype, &bp, &remain); if (kret) return (kret); /* Now default ap_req_sumtype */ kret = krb5_ser_pack_int32((krb5_int32) context->default_ap_req_sumtype, &bp, &remain); if (kret) return (kret); /* Now default safe_sumtype */ kret = krb5_ser_pack_int32((krb5_int32) context->default_safe_sumtype, &bp, &remain); if (kret) return (kret); /* Now kdc_default_options */ kret = krb5_ser_pack_int32((krb5_int32) context->kdc_default_options, &bp, &remain); if (kret) return (kret); /* Now library_options */ kret = krb5_ser_pack_int32((krb5_int32) context->library_options, &bp, &remain); if (kret) return (kret); /* Now profile_secure */ kret = krb5_ser_pack_int32((krb5_int32) context->profile_secure, &bp, &remain); if (kret) return (kret); /* Now fcc_default_format */ kret = krb5_ser_pack_int32((krb5_int32) context->fcc_default_format, &bp, &remain); if (kret) return (kret); /* Now handle os_context, if appropriate */ kret = krb5_externalize_opaque(kcontext, KV5M_OS_CONTEXT, (krb5_pointer) &context->os_context, &bp, &remain); if (kret) return (kret); /* Now handle database context, if appropriate */ if (context->dal_handle) { kret = krb5_externalize_opaque(kcontext, KV5M_DB_CONTEXT, (krb5_pointer) context->dal_handle, &bp, &remain); if (kret) return (kret); } /* Finally, handle profile, if appropriate */ if (context->profile) { kret = krb5_externalize_opaque(kcontext, PROF_MAGIC_PROFILE, (krb5_pointer) context->profile, &bp, &remain); if (kret) return (kret); } /* * If we were successful, write trailer then update the pointer and * remaining length; */ kret = krb5_ser_pack_int32(KV5M_CONTEXT, &bp, &remain); if (kret) return (kret); *buffer = bp; *lenremain = remain; return (0); } /* * krb5_context_internalize() - Internalize the krb5_context. */ static krb5_error_code krb5_context_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain) { krb5_error_code kret; krb5_context context; krb5_int32 ibuf; krb5_octet *bp; size_t remain; unsigned int i, count; bp = *buffer; remain = *lenremain; /* Read our magic number */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) return (EINVAL); if (ibuf != KV5M_CONTEXT) return (EINVAL); /* Get memory for the context */ context = (krb5_context) calloc(1, sizeof(struct _krb5_context)); if (!context) return (ENOMEM); /* Get the size of the default realm */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; if (ibuf) { context->default_realm = (char *) malloc((size_t) ibuf+1); if (!context->default_realm) { kret = ENOMEM; goto cleanup; } kret = krb5_ser_unpack_bytes((krb5_octet *) context->default_realm, (size_t) ibuf, &bp, &remain); if (kret) goto cleanup; context->default_realm[ibuf] = '\0'; } /* Get the in_tkt_etypes */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; count = ibuf; if (count > 0) { context->in_tkt_etypes = calloc(count + 1, sizeof(krb5_enctype)); if (!context->in_tkt_etypes) { kret = ENOMEM; goto cleanup; } for (i = 0; i < count; i++) { if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->in_tkt_etypes[i] = ibuf; } context->in_tkt_etypes[count] = 0; } else context->in_tkt_etypes = NULL; /* Get the tgs_etypes */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; count = ibuf; if (count > 0) { context->tgs_etypes = calloc(count + 1, sizeof(krb5_enctype)); if (!context->tgs_etypes) { kret = ENOMEM; goto cleanup; } for (i = 0; i < count; i++) { if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->tgs_etypes[i] = ibuf; } context->tgs_etypes[count] = 0; } else context->tgs_etypes = NULL; /* Allowable checksum */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->clockskew = (krb5_deltat) ibuf; /* kdc_req_sumtype */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->kdc_req_sumtype = (krb5_cksumtype) ibuf; /* default ap_req_sumtype */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->default_ap_req_sumtype = (krb5_cksumtype) ibuf; /* default_safe_sumtype */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->default_safe_sumtype = (krb5_cksumtype) ibuf; /* kdc_default_options */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->kdc_default_options = (krb5_flags) ibuf; /* library_options */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->library_options = (krb5_flags) ibuf; /* profile_secure */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->profile_secure = (krb5_boolean) ibuf; /* fcc_default_format */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; context->fcc_default_format = (int) ibuf; /* Attempt to read in the os_context. It's an array now, but we still treat it in most places as a separate object with a pointer. */ { krb5_os_context osp = 0; kret = krb5_internalize_opaque(kcontext, KV5M_OS_CONTEXT, (krb5_pointer *) &osp, &bp, &remain); if (kret && (kret != EINVAL) && (kret != ENOENT)) goto cleanup; /* Put the newly allocated data into the krb5_context structure where we're really keeping it these days. */ if (osp) context->os_context = *osp; free(osp); } /* Attempt to read in the db_context */ kret = krb5_internalize_opaque(kcontext, KV5M_DB_CONTEXT, (krb5_pointer *) &context->dal_handle, &bp, &remain); if (kret && (kret != EINVAL) && (kret != ENOENT)) goto cleanup; /* Attempt to read in the profile */ kret = krb5_internalize_opaque(kcontext, PROF_MAGIC_PROFILE, (krb5_pointer *) &context->profile, &bp, &remain); if (kret && (kret != EINVAL) && (kret != ENOENT)) goto cleanup; /* Finally, find the trailer */ if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain))) goto cleanup; if (ibuf != KV5M_CONTEXT) { kret = EINVAL; goto cleanup; } context->magic = KV5M_CONTEXT; *buffer = bp; *lenremain = remain; *argp = (krb5_pointer) context; return 0; cleanup: if (context) krb5_free_context(context); return(kret); } /* * krb5_oscontext_size() - Determine the size required to externalize * the krb5_os_context. */ static krb5_error_code krb5_oscontext_size(krb5_context kcontext, krb5_pointer arg, size_t *sizep) { /* * We need five 32-bit integers: * two for header and trailer * one each for time_offset, usec_offset and os_flags */ *sizep += (5*sizeof(krb5_int32)); return(0); } /* * krb5_oscontext_externalize() - Externalize the krb5_os_context. */ static krb5_error_code krb5_oscontext_externalize(krb5_context kcontext, krb5_pointer arg, krb5_octet **buffer, size_t *lenremain) { krb5_error_code kret; krb5_os_context os_ctx; size_t required; krb5_octet *bp; size_t remain; required = 0; bp = *buffer; remain = *lenremain; kret = EINVAL; if ((os_ctx = (krb5_os_context) arg)) { kret = ENOMEM; if (!krb5_oscontext_size(kcontext, arg, &required) && (required <= remain)) { (void) krb5_ser_pack_int32(KV5M_OS_CONTEXT, &bp, &remain); (void) krb5_ser_pack_int32(os_ctx->time_offset, &bp, &remain); (void) krb5_ser_pack_int32(os_ctx->usec_offset, &bp, &remain); (void) krb5_ser_pack_int32(os_ctx->os_flags, &bp, &remain); (void) krb5_ser_pack_int32(KV5M_OS_CONTEXT, &bp, &remain); /* Handle any other OS context here */ kret = 0; if (!kret) { *buffer = bp; *lenremain = remain; } } } return(kret); } /* * krb5_oscontext_internalize() - Internalize the krb5_os_context. */ static krb5_error_code krb5_oscontext_internalize(krb5_context kcontext, krb5_pointer *argp, krb5_octet **buffer, size_t *lenremain) { krb5_error_code kret; krb5_os_context os_ctx; krb5_int32 ibuf; krb5_octet *bp; size_t remain; bp = *buffer; remain = *lenremain; kret = EINVAL; os_ctx = (krb5_os_context) NULL; /* Read our magic number */ if (krb5_ser_unpack_int32(&ibuf, &bp, &remain)) ibuf = 0; if (ibuf == KV5M_OS_CONTEXT) { kret = ENOMEM; /* Get memory for the context */ if ((os_ctx = (krb5_os_context) calloc(1, sizeof(struct _krb5_os_context))) && (remain >= 4*sizeof(krb5_int32))) { os_ctx->magic = KV5M_OS_CONTEXT; /* Read out our context */ (void) krb5_ser_unpack_int32(&os_ctx->time_offset, &bp, &remain); (void) krb5_ser_unpack_int32(&os_ctx->usec_offset, &bp, &remain); (void) krb5_ser_unpack_int32(&os_ctx->os_flags, &bp, &remain); (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain); if (ibuf == KV5M_OS_CONTEXT) { os_ctx->magic = KV5M_OS_CONTEXT; kret = 0; *buffer = bp; *lenremain = remain; } else kret = EINVAL; } } if (!kret) { *argp = (krb5_pointer) os_ctx; } else { if (os_ctx) free(os_ctx); } return(kret); } /* * Register the context serializers. */ krb5_error_code KRB5_CALLCONV krb5_ser_context_init(krb5_context kcontext) { krb5_error_code kret; kret = krb5_register_serializer(kcontext, &krb5_context_ser_entry); if (!kret) kret = krb5_register_serializer(kcontext, &krb5_oscontext_ser_entry); #ifndef LEAN_CLIENT if (!kret) kret = krb5_register_serializer(kcontext, &krb5_profile_ser_entry); #endif /* LEAN_CLIENT */ return(kret); }