1 /* -*- mode: c; indent-tabs-mode: nil -*- */
2 /*
3 * src/lib/krb5/asn.1/asn1_encode.c
4 *
5 * Copyright 1994, 2008 by the Massachusetts Institute of Technology.
6 * All Rights Reserved.
7 *
8 * Export of this software from the United States of America may
9 * require a specific license from the United States Government.
10 * It is the responsibility of any person or organization contemplating
11 * export to obtain such a license before exporting.
12 *
13 * WITHIN THAT CONSTRAINT, permission to use, copy, modify, and
14 * distribute this software and its documentation for any purpose and
15 * without fee is hereby granted, provided that the above copyright
16 * notice appear in all copies and that both that copyright notice and
17 * this permission notice appear in supporting documentation, and that
18 * the name of M.I.T. not be used in advertising or publicity pertaining
19 * to distribution of the software without specific, written prior
20 * permission. Furthermore if you modify this software you must label
21 * your software as modified software and not distribute it in such a
22 * fashion that it might be confused with the original M.I.T. software.
23 * M.I.T. makes no representations about the suitability of
24 * this software for any purpose. It is provided "as is" without express
25 * or implied warranty.
26 */
27
28 /* ASN.1 primitive encoders */
29
30 #include "asn1_encode.h"
31 #include "asn1_make.h"
32
asn1_encode_boolean(asn1buf * buf,asn1_intmax val,unsigned int * retlen)33 asn1_error_code asn1_encode_boolean(asn1buf *buf, asn1_intmax val,
34 unsigned int *retlen)
35 {
36 asn1_error_code retval;
37 unsigned int length = 0;
38 unsigned int partlen = 1;
39 asn1_octet bval;
40
41 bval = val ? 0xFF : 0x00;
42
43 retval = asn1buf_insert_octet(buf, bval);
44 if (retval) return retval;
45
46 length = partlen;
47 retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_BOOLEAN, length, &partlen);
48 if (retval) return retval;
49 length += partlen;
50
51 *retlen = length;
52 return 0;
53 }
54
asn1_encode_integer_internal(asn1buf * buf,asn1_intmax val,unsigned int * retlen)55 static asn1_error_code asn1_encode_integer_internal(asn1buf *buf,
56 asn1_intmax val,
57 unsigned int *retlen)
58 {
59 asn1_error_code retval;
60 unsigned int length = 0;
61 long valcopy;
62 int digit;
63
64 valcopy = val;
65 do {
66 digit = (int) (valcopy&0xFF);
67 retval = asn1buf_insert_octet(buf,(asn1_octet) digit);
68 if (retval) return retval;
69 length++;
70 valcopy = valcopy >> 8;
71 } while (valcopy != 0 && valcopy != ~0);
72
73 if ((val > 0) && ((digit&0x80) == 0x80)) { /* make sure the high bit is */
74 retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
75 if (retval) return retval;
76 length++;
77 } else if ((val < 0) && ((digit&0x80) != 0x80)) {
78 retval = asn1buf_insert_octet(buf,0xFF);
79 if (retval) return retval;
80 length++;
81 }
82
83
84 *retlen = length;
85 return 0;
86 }
87
asn1_encode_integer(asn1buf * buf,asn1_intmax val,unsigned int * retlen)88 asn1_error_code asn1_encode_integer(asn1buf * buf, asn1_intmax val,
89 unsigned int *retlen)
90 {
91 asn1_error_code retval;
92 unsigned int length = 0;
93 unsigned int partlen;
94 retval = asn1_encode_integer_internal(buf, val, &partlen);
95 if (retval) return retval;
96
97 length = partlen;
98 retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_INTEGER,length, &partlen);
99 if (retval) return retval;
100 length += partlen;
101
102 *retlen = length;
103 return 0;
104 }
105
106 #if 0
107 asn1_error_code
108 asn1_encode_enumerated(asn1buf * buf, long val,
109 unsigned int *retlen)
110 {
111 asn1_error_code retval;
112 unsigned int length = 0;
113 unsigned int partlen;
114 retval = asn1_encode_integer_internal(buf, val, &partlen);
115 if (retval) return retval;
116
117 length = partlen;
118 retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_ENUMERATED,length, &partlen);
119 if (retval) return retval;
120 length += partlen;
121
122 *retlen = length;
123 return 0;
124 }
125 #endif
126
asn1_encode_unsigned_integer(asn1buf * buf,asn1_uintmax val,unsigned int * retlen)127 asn1_error_code asn1_encode_unsigned_integer(asn1buf *buf, asn1_uintmax val,
128 unsigned int *retlen)
129 {
130 asn1_error_code retval;
131 unsigned int length = 0;
132 unsigned int partlen;
133 unsigned long valcopy;
134 int digit;
135
136 valcopy = val;
137 do {
138 digit = (int) (valcopy&0xFF);
139 retval = asn1buf_insert_octet(buf,(asn1_octet) digit);
140 if (retval) return retval;
141 length++;
142 valcopy = valcopy >> 8;
143 } while (valcopy != 0);
144
145 if (digit&0x80) { /* make sure the high bit is */
146 retval = asn1buf_insert_octet(buf,0); /* of the proper signed-ness */
147 if (retval) return retval;
148 length++;
149 }
150
151 retval = asn1_make_tag(buf,UNIVERSAL,PRIMITIVE,ASN1_INTEGER,length, &partlen);
152 if (retval) return retval;
153 length += partlen;
154
155 *retlen = length;
156 return 0;
157 }
158
159 static asn1_error_code
encode_bytestring_with_tag(asn1buf * buf,unsigned int len,const void * val,int tag,unsigned int * retlen)160 encode_bytestring_with_tag(asn1buf *buf, unsigned int len,
161 const void *val, int tag,
162 unsigned int *retlen)
163 {
164 asn1_error_code retval;
165 unsigned int length;
166
167 if (len > 0 && val == 0) return ASN1_MISSING_FIELD;
168 retval = asn1buf_insert_octetstring(buf, len, val);
169 if (retval) return retval;
170 retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, tag,
171 len, &length);
172 if (retval) return retval;
173
174 *retlen = len + length;
175 return 0;
176 }
177
asn1_encode_oid(asn1buf * buf,unsigned int len,const asn1_octet * val,unsigned int * retlen)178 asn1_error_code asn1_encode_oid(asn1buf *buf, unsigned int len,
179 const asn1_octet *val,
180 unsigned int *retlen)
181 {
182 return encode_bytestring_with_tag(buf, len, val, ASN1_OBJECTIDENTIFIER,
183 retlen);
184 }
185
asn1_encode_octetstring(asn1buf * buf,unsigned int len,const void * val,unsigned int * retlen)186 asn1_error_code asn1_encode_octetstring(asn1buf *buf, unsigned int len,
187 const void *val,
188 unsigned int *retlen)
189 {
190 return encode_bytestring_with_tag(buf, len, val, ASN1_OCTETSTRING,
191 retlen);
192 }
193
194 #if 0
195 asn1_error_code asn1_encode_null(asn1buf *buf, int *retlen)
196 {
197 asn1_error_code retval;
198
199 retval = asn1buf_insert_octet(buf,0x00);
200 if (retval) return retval;
201 retval = asn1buf_insert_octet(buf,0x05);
202 if (retval) return retval;
203
204 *retlen = 2;
205 return 0;
206 }
207
208 asn1_error_code asn1_encode_printablestring(asn1buf *buf, unsigned int len,
209 const char *val, int *retlen)
210 {
211 return encode_bytestring_with_tag(buf, len, val, ASN1_PRINTABLESTRING,
212 retlen);
213 }
214
215 asn1_error_code asn1_encode_ia5string(asn1buf *buf, unsigned int len,
216 const char *val, int *retlen)
217 {
218 return encode_bytestring_with_tag(buf, len, val, ASN1_IA5STRING,
219 retlen);
220 }
221 #endif
222
asn1_encode_generaltime(asn1buf * buf,time_t val,unsigned int * retlen)223 asn1_error_code asn1_encode_generaltime(asn1buf *buf, time_t val,
224 unsigned int *retlen)
225 {
226 struct tm *gtime, gtimebuf;
227 char s[16], *sp;
228 time_t gmt_time = val;
229
230 /*
231 * Time encoding: YYYYMMDDhhmmssZ
232 */
233 if (gmt_time == 0) {
234 sp = "19700101000000Z";
235 } else {
236 int len;
237
238 /*
239 * Sanity check this just to be paranoid, as gmtime can return NULL,
240 * and some bogus implementations might overrun on the sprintf.
241 */
242 #ifdef HAVE_GMTIME_R
243 # ifdef GMTIME_R_RETURNS_INT
244 if (gmtime_r(&gmt_time, >imebuf) != 0)
245 return ASN1_BAD_GMTIME;
246 # else
247 if (gmtime_r(&gmt_time, >imebuf) == NULL)
248 return ASN1_BAD_GMTIME;
249 # endif
250 #else
251 gtime = gmtime(&gmt_time);
252 if (gtime == NULL)
253 return ASN1_BAD_GMTIME;
254 memcpy(>imebuf, gtime, sizeof(gtimebuf));
255 #endif
256 gtime = >imebuf;
257
258 if (gtime->tm_year > 8099 || gtime->tm_mon > 11 ||
259 gtime->tm_mday > 31 || gtime->tm_hour > 23 ||
260 gtime->tm_min > 59 || gtime->tm_sec > 59)
261 return ASN1_BAD_GMTIME;
262 len = snprintf(s, sizeof(s), "%04d%02d%02d%02d%02d%02dZ",
263 1900+gtime->tm_year, gtime->tm_mon+1,
264 gtime->tm_mday, gtime->tm_hour,
265 gtime->tm_min, gtime->tm_sec);
266 if (SNPRINTF_OVERFLOW(len, sizeof(s)))
267 /* Shouldn't be possible given above tests. */
268 return ASN1_BAD_GMTIME;
269 sp = s;
270 }
271
272 return encode_bytestring_with_tag(buf, 15, sp, ASN1_GENERALTIME,
273 retlen);
274 }
275
asn1_encode_generalstring(asn1buf * buf,unsigned int len,const void * val,unsigned int * retlen)276 asn1_error_code asn1_encode_generalstring(asn1buf *buf, unsigned int len,
277 const void *val,
278 unsigned int *retlen)
279 {
280 return encode_bytestring_with_tag(buf, len, val, ASN1_GENERALSTRING,
281 retlen);
282 }
283
asn1_encode_bitstring(asn1buf * buf,unsigned int len,const void * val,unsigned int * retlen)284 asn1_error_code asn1_encode_bitstring(asn1buf *buf, unsigned int len,
285 const void *val,
286 unsigned int *retlen)
287 {
288 asn1_error_code retval;
289 unsigned int length;
290
291 retval = asn1buf_insert_octetstring(buf, len, val);
292 if (retval) return retval;
293 retval = asn1buf_insert_octet(buf, 0);
294 if (retval) return retval;
295 retval = asn1_make_tag(buf, UNIVERSAL, PRIMITIVE, ASN1_BITSTRING,
296 len+1, &length);
297 if (retval) return retval;
298 *retlen = len + 1 + length;
299 return 0;
300 }
301
asn1_encode_opaque(asn1buf * buf,unsigned int len,const void * val,unsigned int * retlen)302 asn1_error_code asn1_encode_opaque(asn1buf *buf, unsigned int len,
303 const void *val, unsigned int *retlen)
304 {
305 asn1_error_code retval;
306
307 retval = asn1buf_insert_octetstring(buf, len, val);
308 if (retval) return retval;
309 *retlen = len;
310 return 0;
311 }
312
313 /* ASN.1 constructed type encoder engine
314
315 Two entry points here:
316
317 krb5int_asn1_encode_a_thing: Incrementally adds the partial
318 encoding of an object to an already-initialized asn1buf.
319
320 krb5int_asn1_do_full_encode: Returns a completed encoding, in the
321 correct byte order, in an allocated krb5_data. */
322
323 #ifdef POINTERS_ARE_ALL_THE_SAME
324 #define LOADPTR(PTR,TYPE) \
325 (assert((TYPE)->loadptr != NULL), (TYPE)->loadptr(PTR))
326 #else
327 #define LOADPTR(PTR,TYPE) \
328 (*(const void *const *)(PTR))
329 #endif
330
331 static int
get_nullterm_sequence_len(const void * valp,const struct atype_info * seq)332 get_nullterm_sequence_len(const void *valp, const struct atype_info *seq)
333 {
334 int i;
335 const struct atype_info *a;
336 const void *elt, *eltptr;
337
338 a = seq;
339 i = 0;
340 assert(a->type == atype_ptr);
341 assert(seq->size != 0);
342
343 while (1) {
344 eltptr = (const char *) valp + i * seq->size;
345 elt = LOADPTR(eltptr, a);
346 if (elt == NULL)
347 break;
348 i++;
349 }
350 return i;
351 }
352 static asn1_error_code
353 encode_sequence_of(asn1buf *buf, int seqlen, const void *val,
354 const struct atype_info *eltinfo,
355 unsigned int *retlen);
356
357 static asn1_error_code
encode_nullterm_sequence_of(asn1buf * buf,const void * val,const struct atype_info * type,int can_be_empty,unsigned int * retlen)358 encode_nullterm_sequence_of(asn1buf *buf, const void *val,
359 const struct atype_info *type,
360 int can_be_empty,
361 unsigned int *retlen)
362 {
363 int length = get_nullterm_sequence_len(val, type);
364 if (!can_be_empty && length == 0) return ASN1_MISSING_FIELD;
365 return encode_sequence_of(buf, length, val, type, retlen);
366 }
367
368 static asn1_error_code
369 just_encode_sequence(asn1buf *buf, const void *val,
370 const struct seq_info *seq,
371 unsigned int *retlen);
372 static asn1_error_code
373 encode_a_field(asn1buf *buf, const void *val,
374 const struct field_info *field,
375 unsigned int *retlen);
376
377 asn1_error_code
krb5int_asn1_encode_a_thing(asn1buf * buf,const void * val,const struct atype_info * a,unsigned int * retlen)378 krb5int_asn1_encode_a_thing(asn1buf *buf, const void *val,
379 const struct atype_info *a, unsigned int *retlen)
380 {
381 switch (a->type) {
382 case atype_fn:
383 assert(a->enc != NULL);
384 return a->enc(buf, val, retlen);
385 case atype_sequence:
386 assert(a->seq != NULL);
387 return just_encode_sequence(buf, val, a->seq, retlen);
388 case atype_ptr:
389 assert(a->basetype != NULL);
390 return krb5int_asn1_encode_a_thing(buf, LOADPTR(val, a),
391 a->basetype, retlen);
392 case atype_field:
393 assert(a->field != NULL);
394 return encode_a_field(buf, val, a->field, retlen);
395 case atype_nullterm_sequence_of:
396 case atype_nonempty_nullterm_sequence_of:
397 assert(a->basetype != NULL);
398 return encode_nullterm_sequence_of(buf, val, a->basetype,
399 a->type == atype_nullterm_sequence_of,
400 retlen);
401 case atype_tagged_thing:
402 {
403 asn1_error_code retval;
404 unsigned int length, sum = 0;
405 retval = krb5int_asn1_encode_a_thing(buf, val, a->basetype, &length);
406 if (retval) return retval;
407 sum = length;
408 retval = asn1_make_etag(buf, a->tagtype, a->tagval, sum, &length);
409 if (retval) return retval;
410 sum += length;
411 *retlen = sum;
412 return 0;
413 }
414 case atype_int:
415 assert(a->loadint != NULL);
416 return asn1_encode_integer(buf, a->loadint(val), retlen);
417 case atype_uint:
418 assert(a->loaduint != NULL);
419 return asn1_encode_unsigned_integer(buf, a->loaduint(val), retlen);
420 case atype_min:
421 case atype_max:
422 case atype_fn_len:
423 default:
424 assert(a->type > atype_min);
425 assert(a->type < atype_max);
426 assert(a->type != atype_fn_len);
427 abort();
428 }
429 }
430
431 static asn1_error_code
encode_a_field(asn1buf * buf,const void * val,const struct field_info * field,unsigned int * retlen)432 encode_a_field(asn1buf *buf, const void *val,
433 const struct field_info *field,
434 unsigned int *retlen)
435 {
436 asn1_error_code retval;
437 unsigned int sum = 0;
438
439 if (val == NULL) return ASN1_MISSING_FIELD;
440
441 switch (field->ftype) {
442 case field_immediate:
443 {
444 unsigned int length;
445
446 retval = asn1_encode_integer(buf, (asn1_intmax) field->dataoff,
447 &length);
448 if (retval) return retval;
449 sum += length;
450 break;
451 }
452 case field_sequenceof_len:
453 {
454 const void *dataptr, *lenptr;
455 int slen;
456 unsigned int length;
457 const struct atype_info *a;
458
459 /* The field holds a pointer to the array of objects. So the
460 address we compute is a pointer-to-pointer, and that's what
461 field->atype must help us dereference. */
462 dataptr = (const char *)val + field->dataoff;
463 lenptr = (const char *)val + field->lenoff;
464 assert(field->atype->type == atype_ptr);
465 dataptr = LOADPTR(dataptr, field->atype);
466 a = field->atype->basetype;
467 assert(field->lentype != 0);
468 assert(field->lentype->type == atype_int || field->lentype->type == atype_uint);
469 assert(sizeof(int) <= sizeof(asn1_intmax));
470 assert(sizeof(unsigned int) <= sizeof(asn1_uintmax));
471 if (field->lentype->type == atype_int) {
472 asn1_intmax xlen = field->lentype->loadint(lenptr);
473 if (xlen < 0)
474 return EINVAL;
475 if ((unsigned int) xlen != (asn1_uintmax) xlen)
476 return EINVAL;
477 if ((unsigned int) xlen > INT_MAX)
478 return EINVAL;
479 slen = (int) xlen;
480 } else {
481 asn1_uintmax xlen = field->lentype->loaduint(lenptr);
482 if ((unsigned int) xlen != xlen)
483 return EINVAL;
484 if (xlen > INT_MAX)
485 return EINVAL;
486 slen = (int) xlen;
487 }
488 if (slen != 0 && dataptr == NULL)
489 return ASN1_MISSING_FIELD;
490 retval = encode_sequence_of(buf, slen, dataptr, a, &length);
491 if (retval) return retval;
492 sum += length;
493 break;
494 }
495 case field_normal:
496 {
497 const void *dataptr;
498 const struct atype_info *a;
499 unsigned int length;
500
501 dataptr = (const char *)val + field->dataoff;
502
503 a = field->atype;
504 assert(a->type != atype_fn_len);
505 retval = krb5int_asn1_encode_a_thing(buf, dataptr, a, &length);
506 if (retval) {
507 return retval;
508 }
509 sum += length;
510 break;
511 }
512 case field_string:
513 {
514 const void *dataptr, *lenptr;
515 const struct atype_info *a;
516 size_t slen;
517 unsigned int length;
518
519 dataptr = (const char *)val + field->dataoff;
520 lenptr = (const char *)val + field->lenoff;
521
522 a = field->atype;
523 assert(a->type == atype_fn_len);
524 assert(field->lentype != 0);
525 assert(field->lentype->type == atype_int || field->lentype->type == atype_uint);
526 assert(sizeof(int) <= sizeof(asn1_intmax));
527 assert(sizeof(unsigned int) <= sizeof(asn1_uintmax));
528 if (field->lentype->type == atype_int) {
529 asn1_intmax xlen = field->lentype->loadint(lenptr);
530 if (xlen < 0)
531 return EINVAL;
532 if ((size_t) xlen != (asn1_uintmax) xlen)
533 return EINVAL;
534 slen = (size_t) xlen;
535 } else {
536 asn1_uintmax xlen = field->lentype->loaduint(lenptr);
537 if ((size_t) xlen != xlen)
538 return EINVAL;
539 slen = (size_t) xlen;
540 }
541
542 dataptr = LOADPTR(dataptr, a);
543 if (slen == SIZE_MAX)
544 /* Error - negative or out of size_t range. */
545 return EINVAL;
546 if (dataptr == NULL && slen != 0)
547 return ASN1_MISSING_FIELD;
548 /* Currently our string encoders want "unsigned int" for
549 lengths. */
550 if (slen != (unsigned int) slen)
551 return EINVAL;
552 assert(a->enclen != NULL);
553 retval = a->enclen(buf, (unsigned int) slen, dataptr, &length);
554 if (retval) {
555 return retval;
556 }
557 sum += length;
558 break;
559 }
560 default:
561 assert(field->ftype > field_min);
562 assert(field->ftype < field_max);
563 assert(__LINE__ == 0);
564 abort();
565 }
566 if (field->tag >= 0) {
567 unsigned int length;
568 retval = asn1_make_etag(buf, CONTEXT_SPECIFIC, field->tag, sum,
569 &length);
570 if (retval) {
571 return retval;
572 }
573 sum += length;
574 }
575 *retlen = sum;
576 return 0;
577 }
578
579 static asn1_error_code
encode_fields(asn1buf * buf,const void * val,const struct field_info * fields,size_t nfields,unsigned int optional,unsigned int * retlen)580 encode_fields(asn1buf *buf, const void *val,
581 const struct field_info *fields, size_t nfields,
582 unsigned int optional,
583 unsigned int *retlen)
584 {
585 size_t i;
586 unsigned int sum = 0;
587 for (i = nfields; i > 0; i--) {
588 const struct field_info *f = fields+i-1;
589 unsigned int length;
590 asn1_error_code retval;
591 int present;
592
593 if (f->opt == -1)
594 present = 1;
595 else if ((1u << f->opt) & optional)
596 present = 1;
597 else
598 present = 0;
599 if (present) {
600 retval = encode_a_field(buf, val, f, &length);
601 if (retval) return retval;
602 sum += length;
603 }
604 }
605 *retlen = sum;
606 return 0;
607 }
608
609 static asn1_error_code
just_encode_sequence(asn1buf * buf,const void * val,const struct seq_info * seq,unsigned int * retlen)610 just_encode_sequence(asn1buf *buf, const void *val,
611 const struct seq_info *seq,
612 unsigned int *retlen)
613 {
614 const struct field_info *fields = seq->fields;
615 size_t nfields = seq->n_fields;
616 unsigned int optional;
617 asn1_error_code retval;
618 unsigned int sum = 0;
619
620 if (seq->optional)
621 optional = seq->optional(val);
622 else
623 /* In this case, none of the field descriptors should indicate
624 that we examine any bits of this value. */
625 optional = 0;
626 {
627 unsigned int length;
628 retval = encode_fields(buf, val, fields, nfields, optional, &length);
629 if (retval) return retval;
630 sum += length;
631 }
632 {
633 unsigned int length;
634 retval = asn1_make_sequence(buf, sum, &length);
635 if (retval) return retval;
636 sum += length;
637 }
638 *retlen = sum;
639 return 0;
640 }
641
642 static asn1_error_code
encode_sequence_of(asn1buf * buf,int seqlen,const void * val,const struct atype_info * eltinfo,unsigned int * retlen)643 encode_sequence_of(asn1buf *buf, int seqlen, const void *val,
644 const struct atype_info *eltinfo,
645 unsigned int *retlen)
646 {
647 asn1_error_code retval;
648 unsigned int sum = 0;
649 int i;
650
651 for (i = seqlen-1; i >= 0; i--) {
652 const void *eltptr;
653 unsigned int length;
654 const struct atype_info *a = eltinfo;
655
656 assert(eltinfo->size != 0);
657 eltptr = (const char *)val + i * eltinfo->size;
658 retval = krb5int_asn1_encode_a_thing(buf, eltptr, a, &length);
659 if (retval) return retval;
660 sum += length;
661 }
662 {
663 unsigned int length;
664 retval = asn1_make_sequence(buf, sum, &length);
665 if (retval) return retval;
666 sum += length;
667 }
668 *retlen = sum;
669 return 0;
670 }
671
672 krb5_error_code
krb5int_asn1_do_full_encode(const void * rep,krb5_data ** code,const struct atype_info * a)673 krb5int_asn1_do_full_encode(const void *rep, krb5_data **code,
674 const struct atype_info *a)
675 {
676 unsigned int length;
677 asn1_error_code retval;
678 asn1buf *buf = NULL;
679 krb5_data *d;
680
681 *code = NULL;
682
683 if (rep == NULL)
684 return ASN1_MISSING_FIELD;
685
686 retval = asn1buf_create(&buf);
687 if (retval)
688 return retval;
689
690 retval = krb5int_asn1_encode_a_thing(buf, rep, a, &length);
691 if (retval)
692 goto cleanup;
693 retval = asn12krb5_buf(buf, &d);
694 if (retval)
695 goto cleanup;
696 *code = d;
697 cleanup:
698 asn1buf_destroy(&buf);
699 return retval;
700 }
701