xref: /freebsd/crypto/krb5/src/lib/krb5/krb/ser_ctx.c (revision f1c4c3daccbaf3820f0e2224de53df12fc952fcc)
1 /* -*- mode: c; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /* lib/krb5/krb/ser_ctx.c - Serialize krb5_context structure */
3 /*
4  * Copyright 1995, 2007, 2008 by the Massachusetts Institute of Technology.
5  * All Rights Reserved.
6  *
7  * Export of this software from the United States of America may
8  *   require a specific license from the United States Government.
9  *   It is the responsibility of any person or organization contemplating
10  *   export to obtain such a license before exporting.
11  *
12  * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
13  * distribute this software and its documentation for any purpose and
14  * without fee is hereby granted, provided that the above copyright
15  * notice appear in all copies and that both that copyright notice and
16  * this permission notice appear in supporting documentation, and that
17  * the name of M.I.T. not be used in advertising or publicity pertaining
18  * to distribution of the software without specific, written prior
19  * permission.  Furthermore if you modify this software you must label
20  * your software as modified software and not distribute it in such a
21  * fashion that it might be confused with the original M.I.T. software.
22  * M.I.T. makes no representations about the suitability of
23  * this software for any purpose.  It is provided "as is" without express
24  * or implied warranty.
25  */
26 
27 #include "k5-int.h"
28 #include "int-proto.h"
29 
30 errcode_t profile_ser_size(profile_t, size_t *);
31 errcode_t profile_ser_externalize(profile_t, krb5_octet **, size_t *);
32 errcode_t profile_ser_internalize(profile_t *, krb5_octet **, size_t *);
33 
34 static krb5_error_code size_oscontext(krb5_os_context os_ctx, size_t *sizep);
35 static krb5_error_code externalize_oscontext(krb5_os_context os_ctx,
36                                              krb5_octet **buffer,
37                                              size_t *lenremain);
38 static krb5_error_code internalize_oscontext(krb5_os_context *argp,
39                                              krb5_octet **buffer,
40                                              size_t *lenremain);
41 
42 static inline unsigned int
etypes_len(krb5_enctype * list)43 etypes_len(krb5_enctype *list)
44 {
45     return (list == NULL) ? 0 : k5_count_etypes(list);
46 }
47 
48 krb5_error_code
k5_size_context(krb5_context context,size_t * sizep)49 k5_size_context(krb5_context context, size_t *sizep)
50 {
51     krb5_error_code     kret;
52     size_t              required;
53 
54     /*
55      * The KRB5 context itself requires:
56      *  krb5_int32                      for KV5M_CONTEXT
57      *  krb5_int32                      for sizeof(default_realm)
58      *  strlen(default_realm)           for default_realm.
59      *  krb5_int32                      for n_tgs_etypes*sizeof(krb5_int32)
60      *  nktypes*sizeof(krb5_int32)      for tgs_etypes.
61      *  krb5_int32                      for clockskew
62      *  krb5_int32                      for kdc_default_options
63      *  krb5_int32                      for library_options
64      *  krb5_int32                      for profile_secure
65      *  krb5_int32                      for fcc_default_format
66      *    <>                            for os_context
67      *    <>                            for profile
68      *  krb5_int32                      for trailer.
69      */
70     kret = EINVAL;
71     if (context != NULL) {
72         /* Calculate base length */
73         required = (9 * sizeof(krb5_int32) +
74                     (etypes_len(context->tgs_etypes) * sizeof(krb5_int32)));
75 
76         if (context->default_realm)
77             required += strlen(context->default_realm);
78 
79         /* Calculate size required by os_context, if appropriate */
80         kret = size_oscontext(&context->os_context, &required);
81 
82         /* Calculate size required by profile, if appropriate */
83         if (!kret && context->profile)
84             kret = profile_ser_size(context->profile, &required);
85     }
86     if (!kret)
87         *sizep += required;
88     return(kret);
89 }
90 
91 krb5_error_code
k5_externalize_context(krb5_context context,krb5_octet ** buffer,size_t * lenremain)92 k5_externalize_context(krb5_context context,
93                        krb5_octet **buffer, size_t *lenremain)
94 {
95     krb5_error_code     kret;
96     size_t              required;
97     krb5_octet          *bp;
98     size_t              remain;
99     unsigned int        i;
100 
101     required = 0;
102     bp = *buffer;
103     remain = *lenremain;
104     if (!context)
105         return (EINVAL);
106     if (context->magic != KV5M_CONTEXT)
107         return (KV5M_CONTEXT);
108 
109     if ((kret = k5_size_context(context, &required)))
110         return (kret);
111 
112     if (required > remain)
113         return (ENOMEM);
114 
115     /* First write our magic number */
116     kret = krb5_ser_pack_int32(KV5M_CONTEXT, &bp, &remain);
117     if (kret)
118         return (kret);
119 
120     /* Now sizeof default realm */
121     kret = krb5_ser_pack_int32((context->default_realm) ?
122                                (krb5_int32) strlen(context->default_realm) : 0,
123                                &bp, &remain);
124     if (kret)
125         return (kret);
126 
127     /* Now default_realm bytes */
128     if (context->default_realm) {
129         kret = krb5_ser_pack_bytes((krb5_octet *) context->default_realm,
130                                    strlen(context->default_realm),
131                                    &bp, &remain);
132         if (kret)
133             return (kret);
134     }
135 
136     /* Now number of default ktypes */
137     kret = krb5_ser_pack_int32(etypes_len(context->tgs_etypes), &bp, &remain);
138     if (kret)
139         return (kret);
140 
141     /* Now serialize ktypes */
142     if (context->tgs_etypes) {
143         for (i = 0; context->tgs_etypes[i]; i++) {
144             kret = krb5_ser_pack_int32(context->tgs_etypes[i], &bp, &remain);
145             if (kret)
146                 return (kret);
147         }
148     }
149 
150     /* Now allowable clockskew */
151     kret = krb5_ser_pack_int32((krb5_int32) context->clockskew,
152                                &bp, &remain);
153     if (kret)
154         return (kret);
155 
156     /* Now kdc_default_options */
157     kret = krb5_ser_pack_int32((krb5_int32) context->kdc_default_options,
158                                &bp, &remain);
159     if (kret)
160         return (kret);
161 
162     /* Now library_options */
163     kret = krb5_ser_pack_int32((krb5_int32) context->library_options,
164                                &bp, &remain);
165     if (kret)
166         return (kret);
167 
168     /* Now profile_secure */
169     kret = krb5_ser_pack_int32((krb5_int32) context->profile_secure,
170                                &bp, &remain);
171     if (kret)
172         return (kret);
173 
174     /* Now fcc_default_format */
175     kret = krb5_ser_pack_int32((krb5_int32) context->fcc_default_format,
176                                &bp, &remain);
177     if (kret)
178         return (kret);
179 
180     /* Now handle os_context, if appropriate */
181     kret = externalize_oscontext(&context->os_context, &bp, &remain);
182     if (kret)
183         return (kret);
184 
185     /* Finally, handle profile, if appropriate */
186     if (context->profile != NULL) {
187         kret = profile_ser_externalize(context->profile, &bp, &remain);
188         if (kret)
189             return (kret);
190     }
191 
192     /*
193      * If we were successful, write trailer then update the pointer and
194      * remaining length;
195      */
196     kret = krb5_ser_pack_int32(KV5M_CONTEXT, &bp, &remain);
197     if (kret)
198         return (kret);
199 
200     *buffer = bp;
201     *lenremain = remain;
202 
203     return (0);
204 }
205 
206 krb5_error_code
k5_internalize_context(krb5_context * argp,krb5_octet ** buffer,size_t * lenremain)207 k5_internalize_context(krb5_context *argp,
208                        krb5_octet **buffer, size_t *lenremain)
209 {
210     krb5_error_code     kret;
211     krb5_context        context;
212     krb5_int32          ibuf;
213     krb5_octet          *bp;
214     size_t              remain;
215     unsigned int        i, count;
216 
217     bp = *buffer;
218     remain = *lenremain;
219 
220     /* Read our magic number */
221     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
222         return (EINVAL);
223 
224     if (ibuf != KV5M_CONTEXT)
225         return (EINVAL);
226 
227     /* Get memory for the context */
228     context = (krb5_context) calloc(1, sizeof(struct _krb5_context));
229     if (!context)
230         return (ENOMEM);
231 
232     /* Get the size of the default realm */
233     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
234         goto cleanup;
235 
236     if (ibuf) {
237         context->default_realm = (char *) malloc((size_t) ibuf+1);
238         if (!context->default_realm) {
239             kret = ENOMEM;
240             goto cleanup;
241         }
242 
243         kret = krb5_ser_unpack_bytes((krb5_octet *) context->default_realm,
244                                      (size_t) ibuf, &bp, &remain);
245         if (kret)
246             goto cleanup;
247 
248         context->default_realm[ibuf] = '\0';
249     }
250 
251     /* Get the tgs_etypes */
252     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
253         goto cleanup;
254     count = ibuf;
255     if (count > 0) {
256         context->tgs_etypes = calloc(count + 1, sizeof(krb5_enctype));
257         if (!context->tgs_etypes) {
258             kret = ENOMEM;
259             goto cleanup;
260         }
261         for (i = 0; i < count; i++) {
262             if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
263                 goto cleanup;
264             context->tgs_etypes[i] = ibuf;
265         }
266         context->tgs_etypes[count] = 0;
267     } else
268         context->tgs_etypes = NULL;
269 
270     /* Allowable clockskew */
271     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
272         goto cleanup;
273     context->clockskew = (krb5_deltat) ibuf;
274 
275     /* kdc_default_options */
276     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
277         goto cleanup;
278     context->kdc_default_options = (krb5_flags) ibuf;
279 
280     /* library_options */
281     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
282         goto cleanup;
283     context->library_options = (krb5_flags) ibuf;
284 
285     /* profile_secure */
286     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
287         goto cleanup;
288     context->profile_secure = (krb5_boolean) ibuf;
289 
290     /* fcc_default_format */
291     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
292         goto cleanup;
293     context->fcc_default_format = (int) ibuf;
294 
295     /* Attempt to read in the os_context.  It's an array now, but
296        we still treat it in most places as a separate object with
297        a pointer.  */
298     {
299         krb5_os_context osp = 0;
300         kret = internalize_oscontext(&osp, &bp, &remain);
301         if (kret && (kret != EINVAL) && (kret != ENOENT))
302             goto cleanup;
303         /* Put the newly allocated data into the krb5_context
304            structure where we're really keeping it these days.  */
305         if (osp)
306             context->os_context = *osp;
307         free(osp);
308     }
309 
310     /* Attempt to read in the profile */
311     kret = profile_ser_internalize(&context->profile, &bp, &remain);
312     if (kret && (kret != EINVAL) && (kret != ENOENT))
313         goto cleanup;
314 
315     /* Finally, find the trailer */
316     if ((kret = krb5_ser_unpack_int32(&ibuf, &bp, &remain)))
317         goto cleanup;
318 
319     if (ibuf != KV5M_CONTEXT) {
320         kret = EINVAL;
321         goto cleanup;
322     }
323 
324     context->magic = KV5M_CONTEXT;
325     *buffer = bp;
326     *lenremain = remain;
327     *argp = context;
328 
329     return 0;
330 
331 cleanup:
332     if (context)
333         krb5_free_context(context);
334     return(kret);
335 }
336 
337 krb5_error_code
size_oscontext(krb5_os_context os_ctx,size_t * sizep)338 size_oscontext(krb5_os_context os_ctx, size_t *sizep)
339 {
340     /*
341      * We need five 32-bit integers:
342      *  two for header and trailer
343      *  one each for time_offset, usec_offset and os_flags
344      */
345     *sizep += (5*sizeof(krb5_int32));
346     return(0);
347 }
348 
349 krb5_error_code
externalize_oscontext(krb5_os_context os_ctx,krb5_octet ** buffer,size_t * lenremain)350 externalize_oscontext(krb5_os_context os_ctx,
351                       krb5_octet **buffer, size_t *lenremain)
352 {
353     krb5_error_code     kret;
354     size_t              required;
355     krb5_octet          *bp;
356     size_t              remain;
357 
358     required = 0;
359     bp = *buffer;
360     remain = *lenremain;
361     kret = EINVAL;
362     if (os_ctx != NULL) {
363         kret = ENOMEM;
364         if (!size_oscontext(os_ctx, &required) && required <= remain) {
365             (void) krb5_ser_pack_int32(KV5M_OS_CONTEXT, &bp, &remain);
366             (void) krb5_ser_pack_int32(os_ctx->time_offset, &bp, &remain);
367             (void) krb5_ser_pack_int32(os_ctx->usec_offset, &bp, &remain);
368             (void) krb5_ser_pack_int32(os_ctx->os_flags, &bp, &remain);
369             (void) krb5_ser_pack_int32(KV5M_OS_CONTEXT, &bp, &remain);
370 
371             /* Handle any other OS context here */
372             kret = 0;
373             if (!kret) {
374                 *buffer = bp;
375                 *lenremain = remain;
376             }
377         }
378     }
379     return(kret);
380 }
381 
382 static krb5_error_code
internalize_oscontext(krb5_os_context * argp,krb5_octet ** buffer,size_t * lenremain)383 internalize_oscontext(krb5_os_context *argp,
384                       krb5_octet **buffer, size_t *lenremain)
385 {
386     krb5_error_code     kret;
387     krb5_os_context     os_ctx;
388     krb5_int32          ibuf;
389     krb5_octet          *bp;
390     size_t              remain;
391 
392     bp = *buffer;
393     remain = *lenremain;
394     kret = EINVAL;
395     os_ctx = (krb5_os_context) NULL;
396     /* Read our magic number */
397     if (krb5_ser_unpack_int32(&ibuf, &bp, &remain))
398         ibuf = 0;
399     if (ibuf == KV5M_OS_CONTEXT) {
400         kret = ENOMEM;
401 
402         /* Get memory for the context */
403         if ((os_ctx = (krb5_os_context)
404              calloc(1, sizeof(struct _krb5_os_context))) &&
405             (remain >= 4*sizeof(krb5_int32))) {
406             os_ctx->magic = KV5M_OS_CONTEXT;
407 
408             /* Read out our context */
409             (void) krb5_ser_unpack_int32(&os_ctx->time_offset, &bp, &remain);
410             (void) krb5_ser_unpack_int32(&os_ctx->usec_offset, &bp, &remain);
411             (void) krb5_ser_unpack_int32(&os_ctx->os_flags, &bp, &remain);
412             (void) krb5_ser_unpack_int32(&ibuf, &bp, &remain);
413 
414             if (ibuf == KV5M_OS_CONTEXT) {
415                 os_ctx->magic = KV5M_OS_CONTEXT;
416                 kret = 0;
417                 *buffer = bp;
418                 *lenremain = remain;
419             } else
420                 kret = EINVAL;
421         }
422     }
423     if (!kret) {
424         *argp = os_ctx;
425     }
426     else {
427         if (os_ctx)
428             free(os_ctx);
429     }
430     return(kret);
431 }
432