added files
[bcm963xx.git] / userapps / opensource / net-snmp / snmplib / snmpksm.c
1 /*
2  * snmpksm.c
3  *
4  * This code implements the Kerberos Security Model (KSM) for SNMP.
5  *
6  * Security number - 2066432
7  */
8
9 #include <net-snmp/net-snmp-config.h>
10
11 #include <sys/types.h>
12 #if HAVE_WINSOCK_H
13 #include <winsock.h>
14 #endif
15 #include <stdio.h>
16 #ifdef HAVE_STDLIB_H
17 #include <stdlib.h>
18 #endif
19 #if TIME_WITH_SYS_TIME
20 # ifdef WIN32
21 #  include <sys/timeb.h>
22 # else
23 #  include <sys/time.h>
24 # endif
25 # include <time.h>
26 #else
27 # if HAVE_SYS_TIME_H
28 #  include <sys/time.h>
29 # else
30 #  include <time.h>
31 # endif
32 #endif
33 #if HAVE_STRING_H
34 #include <string.h>
35 #else
36 #include <strings.h>
37 #endif
38 #ifdef HAVE_NETINET_IN_H
39 #include <netinet/in.h>
40 #endif
41 #include <errno.h>
42
43
44 #if HAVE_DMALLOC_H
45 #include <dmalloc.h>
46 #endif
47
48 #include <krb5.h>
49 #include <com_err.h>
50
51 #include <net-snmp/output_api.h>
52 #include <net-snmp/config_api.h>
53 #include <net-snmp/utilities.h>
54
55 #include <net-snmp/library/asn1.h>
56 #include <net-snmp/library/snmp_api.h>
57 #include <net-snmp/library/callback.h>
58 #include <net-snmp/library/keytools.h>
59 #include <net-snmp/library/snmpv3.h>
60 #include <net-snmp/library/lcd_time.h>
61 #include <net-snmp/library/scapi.h>
62 #include <net-snmp/library/callback.h>
63 #include <net-snmp/library/snmp_secmod.h>
64 #include <net-snmp/library/snmpksm.h>
65
66 static krb5_context kcontext = NULL;
67 static krb5_rcache rcache = NULL;
68
69 static int      ksm_session_init(netsnmp_session *);
70 static void     ksm_free_state_ref(void *);
71 static int      ksm_free_pdu(netsnmp_pdu *);
72 static int      ksm_clone_pdu(netsnmp_pdu *, netsnmp_pdu *);
73
74 static int      ksm_insert_cache(long, krb5_auth_context, u_char *,
75                                  size_t);
76 static void     ksm_decrement_ref_count(long);
77 static void     ksm_increment_ref_count(long);
78 static struct ksm_cache_entry *ksm_get_cache(long);
79
80 #define HASHSIZE        64
81
82 /*
83  * Our information stored for the response PDU.
84  */
85
86 struct ksm_secStateRef {
87     krb5_auth_context auth_context;
88     krb5_cksumtype  cksumtype;
89 };
90
91 /*
92  * A KSM outgoing pdu cache entry
93  */
94
95 struct ksm_cache_entry {
96     long            msgid;
97     int             refcount;
98     krb5_auth_context auth_context;
99     u_char         *secName;
100     size_t          secNameLen;
101     struct ksm_cache_entry *next;
102 };
103
104 /*
105  * Poor man's hash table
106  */
107
108 static struct ksm_cache_entry *ksm_hash_table[HASHSIZE];
109
110 /*
111  * Initialize all of the state required for Kerberos (right now, just call
112  * krb5_init_context).
113  */
114
115 void
116 init_ksm(void)
117 {
118     krb5_error_code retval;
119     struct snmp_secmod_def *def;
120     int             i;
121
122     if (kcontext == NULL) {
123         retval = krb5_init_context(&kcontext);
124
125         if (retval) {
126             DEBUGMSGTL(("ksm", "krb5_init_context failed (%s), not "
127                         "registering KSM\n", error_message(retval)));
128             return;
129         }
130     }
131
132     for (i = 0; i < HASHSIZE; i++)
133         ksm_hash_table[i] = NULL;
134
135     def = SNMP_MALLOC_STRUCT(snmp_secmod_def);
136
137     if (!def) {
138         DEBUGMSGTL(("ksm", "Unable to malloc snmp_secmod struct, not "
139                     "registering KSM\n"));
140         return;
141     }
142
143     def->encode_reverse = ksm_rgenerate_out_msg;
144     def->decode = ksm_process_in_msg;
145     def->session_open = ksm_session_init;
146     def->pdu_free_state_ref = ksm_free_state_ref;
147     def->pdu_free = ksm_free_pdu;
148     def->pdu_clone = ksm_clone_pdu;
149
150     register_sec_mod(2066432, "ksm", def);
151 }
152
153 /*
154  * These routines implement a simple cache for information we need to
155  * process responses.  When we send out a request, it contains an AP_REQ;
156  * we get back an AP_REP, and we need the authorization context from the
157  * AP_REQ to decrypt the AP_REP.  But because right now there's nothing
158  * that gets preserved across calls to rgenerate_out_msg to process_in_msg,
159  * we cache these internally based on the message ID (we also cache the
160  * passed-in security name, for reasons that are mostly stupid).
161  */
162
163 static int
164 ksm_insert_cache(long msgid, krb5_auth_context auth_context,
165                  u_char * secName, size_t secNameLen)
166 {
167     struct ksm_cache_entry *entry;
168     int             bucket;
169     int             retval;
170
171     entry = SNMP_MALLOC_STRUCT(ksm_cache_entry);
172
173     if (!entry)
174         return SNMPERR_MALLOC;
175
176     entry->msgid = msgid;
177     entry->auth_context = auth_context;
178     entry->refcount = 1;
179
180     retval = memdup(&entry->secName, secName, secNameLen);
181
182     if (retval != SNMPERR_SUCCESS) {
183         free(entry);
184         return retval;
185     }
186
187     entry->secNameLen = secNameLen;
188
189     bucket = msgid % HASHSIZE;
190
191     entry->next = ksm_hash_table[bucket];
192     ksm_hash_table[bucket] = entry;
193
194     return SNMPERR_SUCCESS;
195 }
196
197 static struct ksm_cache_entry *
198 ksm_get_cache(long msgid)
199 {
200     struct ksm_cache_entry *entry;
201     int             bucket;
202
203     bucket = msgid % HASHSIZE;
204
205     for (entry = ksm_hash_table[bucket]; entry != NULL;
206          entry = entry->next)
207         if (entry->msgid == msgid)
208             return entry;
209
210     return NULL;
211 }
212
213 static void
214 ksm_decrement_ref_count(long msgid)
215 {
216     struct ksm_cache_entry *entry, *entry1;
217     int             bucket;
218
219     bucket = msgid % HASHSIZE;
220
221     if (ksm_hash_table[bucket] && ksm_hash_table[bucket]->msgid == msgid) {
222         entry = ksm_hash_table[bucket];
223
224         /*
225          * If the reference count is zero, then free it
226          */
227
228         if (--entry->refcount <= 0) {
229             DEBUGMSGTL(("ksm", "Freeing entry for msgid %ld\n", msgid));
230             krb5_auth_con_free(kcontext, entry->auth_context);
231             free(entry->secName);
232             ksm_hash_table[bucket] = entry->next;
233             free(entry);
234         }
235
236         return;
237
238     } else if (ksm_hash_table[bucket])
239         for (entry1 = ksm_hash_table[bucket], entry = entry1->next;
240              entry != NULL; entry1 = entry, entry = entry->next)
241             if (entry->msgid == msgid) {
242
243                 if (--entry->refcount <= 0) {
244                     DEBUGMSGTL(("ksm", "Freeing entry for msgid %ld\n",
245                                 msgid));
246                     krb5_auth_con_free(kcontext, entry->auth_context);
247                     free(entry->secName);
248                     entry1->next = entry->next;
249                     free(entry);
250                 }
251
252                 return;
253             }
254
255     DEBUGMSGTL(("ksm",
256                 "KSM: Unable to decrement cache entry for msgid %ld.\n",
257                 msgid));
258 }
259
260 static void
261 ksm_increment_ref_count(long msgid)
262 {
263     struct ksm_cache_entry *entry = ksm_get_cache(msgid);
264
265     if (!entry) {
266         DEBUGMSGTL(("ksm", "Unable to find cache entry for msgid %ld "
267                     "for increment\n", msgid));
268         return;
269     }
270
271     entry->refcount++;
272 }
273
274 /*
275  * Initialize specific session information (right now, just set up things to
276  * not do an engineID probe)
277  */
278
279 static int
280 ksm_session_init(netsnmp_session * sess)
281 {
282     DEBUGMSGTL(("ksm",
283                 "KSM: Reached our session initialization callback\n"));
284
285     sess->flags |= SNMP_FLAGS_DONT_PROBE;
286
287     return SNMPERR_SUCCESS;
288 }
289
290 /*
291  * Free our state information (this is only done on the agent side)
292  */
293
294 static void
295 ksm_free_state_ref(void *ptr)
296 {
297     struct ksm_secStateRef *ref = (struct ksm_secStateRef *) ptr;
298
299     DEBUGMSGTL(("ksm", "KSM: Freeing state reference\n"));
300
301     krb5_auth_con_free(kcontext, ref->auth_context);
302
303     free(ref);
304 }
305
306 /*
307  * This is called when the PDU is freed; this will decrement reference counts
308  * for entries in our state cache.
309  */
310
311 static int
312 ksm_free_pdu(netsnmp_pdu *pdu)
313 {
314     ksm_decrement_ref_count(pdu->msgid);
315
316     DEBUGMSGTL(("ksm", "Decrementing cache entry for PDU msgid %ld\n",
317                 pdu->msgid));
318
319     return SNMPERR_SUCCESS;
320 }
321
322 /*
323  * This is called when a PDU is cloned (to increase reference counts)
324  */
325
326 static int
327 ksm_clone_pdu(netsnmp_pdu *pdu, netsnmp_pdu *pdu2)
328 {
329     ksm_increment_ref_count(pdu->msgid);
330
331     DEBUGMSGTL(("ksm", "Incrementing cache entry for PDU msgid %ld\n",
332                 pdu->msgid));
333
334     return SNMPERR_SUCCESS;
335 }
336
337 /****************************************************************************
338  *
339  * ksm_generate_out_msg
340  *
341  * Parameters:
342  *      (See list below...)
343  *
344  * Returns:
345  *      SNMPERR_GENERIC                        On success.
346  *      SNMPERR_KRB5
347  *      ... and others
348  *
349  *
350  * Generate an outgoing message.
351  *
352  ****************************************************************************/
353
354 int
355 ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms)
356 {
357     krb5_auth_context auth_context = NULL;
358     krb5_error_code retcode;
359     krb5_ccache     cc = NULL;
360     int             retval = SNMPERR_SUCCESS;
361     krb5_data       outdata, ivector;
362     krb5_keyblock  *subkey = NULL;
363 #ifdef MIT_NEW_CRYPTO
364     krb5_data       input;
365     krb5_enc_data   output;
366 #else                           /* MIT_NEW_CRYPTO */
367     krb5_encrypt_block eblock;
368 #endif                          /* MIT_NEW_CRYPTO */
369     size_t          blocksize, encrypted_length;
370     unsigned char  *encrypted_data = NULL;
371     int             zero = 0, i;
372     u_char         *cksum_pointer, *endp = *parms->wholeMsg;
373     krb5_cksumtype  cksumtype = CKSUMTYPE_RSA_MD5_DES;
374     krb5_checksum   pdu_checksum;
375     u_char         **wholeMsg = parms->wholeMsg;
376     size_t         *offset = parms->wholeMsgOffset, seq_offset;
377     struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *)
378         parms->secStateRef;
379     int rc;
380
381     DEBUGMSGTL(("ksm", "Starting KSM processing\n"));
382
383     outdata.length = 0;
384     outdata.data = NULL;
385     ivector.length = 0;
386     ivector.data = NULL;
387     pdu_checksum.contents = NULL;
388
389     if (!ksm_state) {
390         /*
391          * If we don't have a ksm_state, then we're a request.  Get a
392          * credential cache and build a ap_req.
393          */
394         retcode = krb5_cc_default(kcontext, &cc);
395
396         if (retcode) {
397             DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n",
398                         error_message(retcode)));
399             snmp_set_detail(error_message(retcode));
400             retval = SNMPERR_KRB5;
401             goto error;
402         }
403
404         DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n"));
405
406         /*
407          * This seems odd, since we don't need this until later (or earlier,
408          * depending on how you look at it), but because the most likely
409          * errors are Kerberos at this point, I'll get this now to save
410          * time not encoding the rest of the packet.
411          *
412          * Also, we need the subkey to encrypt the PDU (if required).
413          */
414
415         retcode =
416             krb5_mk_req(kcontext, &auth_context,
417                         AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
418                         (char *) "host", parms->session->peername, NULL,
419                         cc, &outdata);
420
421         if (retcode) {
422             DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n",
423                         error_message(retcode)));
424             snmp_set_detail(error_message(retcode));
425             retval = SNMPERR_KRB5;
426             goto error;
427         }
428
429         DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully\n"));
430
431     } else {
432
433         /*
434          * Grab the auth_context from our security state reference
435          */
436
437         auth_context = ksm_state->auth_context;
438
439         /*
440          * Bundle up an AP_REP.  Note that we do this only when we
441          * have a security state reference (which means we're in an agent
442          * and we're sending a response).
443          */
444
445         DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n"));
446
447         retcode = krb5_mk_rep(kcontext, auth_context, &outdata);
448
449         if (retcode) {
450             DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n",
451                         error_message(retcode)));
452             snmp_set_detail(error_message(retcode));
453             retval = SNMPERR_KRB5;
454             goto error;
455         }
456
457         DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n"));
458     }
459
460     /*
461      * If we have to encrypt the PDU, do that now
462      */
463
464     if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
465
466         DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n"));
467
468         /*
469          * It's weird -
470          *
471          * If we're on the manager, it's a local subkey (because that's in
472          * our AP_REQ)
473          *
474          * If we're on the agent, it's a remote subkey (because that comes
475          * FROM the received AP_REQ).
476          */
477
478         if (ksm_state)
479             retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
480                                                     &subkey);
481         else
482             retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
483                                                    &subkey);
484
485         if (retcode) {
486             DEBUGMSGTL(("ksm",
487                         "KSM: krb5_auth_con_getlocalsubkey failed: %s\n",
488                         error_message(retcode)));
489             snmp_set_detail(error_message(retcode));
490             retval = SNMPERR_KRB5;
491             goto error;
492         }
493
494         /*
495          * Note that here we need to handle different things between the
496          * old and new crypto APIs.  First, we need to get the final encrypted
497          * length of the PDU.
498          */
499
500 #ifdef MIT_NEW_CRYPTO
501         retcode = krb5_c_encrypt_length(kcontext, subkey->enctype,
502                                         parms->scopedPduLen,
503                                         &encrypted_length);
504
505         if (retcode) {
506             DEBUGMSGTL(("ksm",
507                         "Encryption length calculation failed: %s\n",
508                         error_message(retcode)));
509             snmp_set_detail(error_message(retcode));
510             retval = SNMPERR_KRB5;
511             goto error;
512         }
513 #else                           /* MIT_NEW_CRYPTO */
514
515         krb5_use_enctype(kcontext, &eblock, subkey->enctype);
516         retcode = krb5_process_key(kcontext, &eblock, subkey);
517
518         if (retcode) {
519             DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n",
520                         error_message(retcode)));
521             snmp_set_detail(error_message(retcode));
522             retval = SNMPERR_KRB5;
523             goto error;
524         }
525
526         encrypted_length = krb5_encrypt_size(parms->scopedPduLen,
527                                              eblock.crypto_entry);
528 #endif                          /* MIT_NEW_CRYPTO */
529
530         encrypted_data = malloc(encrypted_length);
531
532         if (!encrypted_data) {
533             DEBUGMSGTL(("ksm",
534                         "KSM: Unable to malloc %d bytes for encrypt "
535                         "buffer: %s\n", parms->scopedPduLen,
536                         strerror(errno)));
537             retval = SNMPERR_MALLOC;
538 #ifndef MIT_NEW_CRYPTO
539             krb5_finish_key(kcontext, &eblock);
540 #endif                          /* ! MIT_NEW_CRYPTO */
541
542             goto error;
543         }
544
545         /*
546          * We need to set up a blank initialization vector for the encryption.
547          * Use a block of all zero's (which is dependent on the block size
548          * of the encryption method).
549          */
550
551 #ifdef MIT_NEW_CRYPTO
552
553         retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);
554
555         if (retcode) {
556             DEBUGMSGTL(("ksm",
557                         "Unable to determine crypto block size: %s\n",
558                         error_message(retcode)));
559             snmp_set_detail(error_message(retcode));
560             retval = SNMPERR_KRB5;
561             goto error;
562         }
563 #else                           /* MIT_NEW_CRYPTO */
564
565         blocksize =
566             krb5_enctype_array[subkey->enctype]->system->block_length;
567
568 #endif                          /* MIT_NEW_CRYPTO */
569
570         ivector.data = malloc(blocksize);
571
572         if (!ivector.data) {
573             DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
574                         blocksize));
575             retval = SNMPERR_MALLOC;
576             goto error;
577         }
578
579         ivector.length = blocksize;
580         memset(ivector.data, 0, blocksize);
581
582         /*
583          * Finally!  Do the encryption!
584          */
585
586 #ifdef MIT_NEW_CRYPTO
587
588         input.data = (char *) parms->scopedPdu;
589         input.length = parms->scopedPduLen;
590         output.ciphertext.data = (char *) encrypted_data;
591         output.ciphertext.length = encrypted_length;
592
593         retcode =
594             krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
595                            &ivector, &input, &output);
596
597 #else                           /* MIT_NEW_CRYPTO */
598
599         retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu,
600                                (krb5_pointer) encrypted_data,
601                                parms->scopedPduLen, &eblock, ivector.data);
602
603         krb5_finish_key(kcontext, &eblock);
604
605 #endif                          /* MIT_NEW_CRYPTO */
606
607         if (retcode) {
608             DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n",
609                         error_message(retcode)));
610             retval = SNMPERR_KRB5;
611             snmp_set_detail(error_message(retcode));
612             goto error;
613         }
614
615         *offset = 0;
616
617         rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
618                                              offset, 1,
619                                              (u_char) (ASN_UNIVERSAL |
620                                                        ASN_PRIMITIVE |
621                                                        ASN_OCTET_STR),
622                                              encrypted_data,
623                                              encrypted_length);
624
625         if (rc == 0) {
626             DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n"));
627             retval = SNMPERR_TOO_LONG;
628             goto error;
629         }
630
631         DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n"));
632
633     } else {
634         /*
635          * Plaintext PDU (not encrypted)
636          */
637
638         if (*parms->wholeMsgLen < parms->scopedPduLen) {
639             DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n"));
640             retval = SNMPERR_TOO_LONG;
641             goto error;
642         }
643     }
644
645     /*
646      * Start encoding the msgSecurityParameters
647      *
648      * For now, use 0 for the response hint
649      */
650
651     DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n"));
652
653     seq_offset = *offset;
654
655     rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
656                                       offset, 1,
657                                       (u_char) (ASN_UNIVERSAL |
658                                                 ASN_PRIMITIVE |
659                                                 ASN_INTEGER),
660                                       (long *) &zero, sizeof(zero));
661
662     if (rc == 0) {
663         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
664         retval = SNMPERR_TOO_LONG;
665         goto error;
666     }
667
668     rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
669                                          offset, 1,
670                                          (u_char) (ASN_UNIVERSAL |
671                                                    ASN_PRIMITIVE |
672                                                    ASN_OCTET_STR),
673                                          (u_char *) outdata.data,
674                                          outdata.length);
675
676     if (rc == 0) {
677         DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n"));
678         retval = SNMPERR_TOO_LONG;
679         goto error;
680     }
681
682     /*
683      * Hardcode checksum type FOR NOW! XXX (but make sure the response
684      * checksum type is the same as the request).
685      *
686      * Not sure what we're supposed to do about checksum negotiation.
687      */
688
689     if (ksm_state)
690         cksumtype = ksm_state->cksumtype;
691
692     if (!is_keyed_cksum(cksumtype)) {
693         DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
694                     cksumtype));
695         snmp_set_detail("Checksum is not a keyed checksum");
696         retval = SNMPERR_KRB5;
697         goto error;
698     }
699
700     if (!is_coll_proof_cksum(cksumtype)) {
701         DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
702                     "checksum\n", cksumtype));
703         snmp_set_detail("Checksum is not a collision-proof checksum");
704         retval = SNMPERR_KRB5;
705         goto error;
706     }
707 #ifdef MIT_NEW_CRYPTO
708     retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize);
709
710     if (retcode) {
711         DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n",
712                     error_message(retcode)));
713         snmp_set_detail(error_message(retcode));
714         retval = SNMPERR_KRB5;
715         goto error;
716     }
717
718     pdu_checksum.length = blocksize;
719
720 #else                           /* MIT_NEW_CRYPTO */
721
722     pdu_checksum.length = krb5_checksum_size(kcontext, cksumtype);
723     pdu_checksum.checksum_type = cksumtype;
724
725 #endif                          /* MIT_NEW_CRYPTO */
726
727     /*
728      * Note that here, we're just leaving blank space for the checksum;
729      * we remember where that is, and we'll fill it in later.
730      */
731
732     *offset += pdu_checksum.length;
733     memset(*wholeMsg + *parms->wholeMsgLen - *offset, 0, pdu_checksum.length);
734
735     cksum_pointer = *wholeMsg + *parms->wholeMsgLen - *offset;
736
737     rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
738                                          parms->wholeMsgOffset, 1,
739                                          (u_char) (ASN_UNIVERSAL |
740                                                    ASN_PRIMITIVE |
741                                                    ASN_OCTET_STR),
742                                          pdu_checksum.length);
743
744     if (rc == 0) {
745         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
746         retval = SNMPERR_TOO_LONG;
747         goto error;
748     }
749
750     rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
751                                       parms->wholeMsgOffset, 1,
752                                       (u_char) (ASN_UNIVERSAL |
753                                                 ASN_PRIMITIVE |
754                                                 ASN_OCTET_STR),
755                                       (long *) &cksumtype,
756                                       sizeof(cksumtype));
757
758     if (rc == 0) {
759         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
760         retval = SNMPERR_TOO_LONG;
761         goto error;
762     }
763
764     rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
765                                            parms->wholeMsgOffset, 1,
766                                            (u_char) (ASN_SEQUENCE |
767                                                      ASN_CONSTRUCTOR),
768                                            *offset - seq_offset);
769
770     if (rc == 0) {
771         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
772         retval = SNMPERR_TOO_LONG;
773         goto error;
774     }
775
776     rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
777                                          parms->wholeMsgOffset, 1,
778                                          (u_char) (ASN_UNIVERSAL |
779                                                    ASN_PRIMITIVE |
780                                                    ASN_OCTET_STR),
781                                          *offset - seq_offset);
782
783     if (rc == 0) {
784         DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
785         retval = SNMPERR_TOO_LONG;
786         goto error;
787     }
788
789     DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n"));
790
791     /*
792      * We're done with the KSM security parameters - now we do the global
793      * header and wrap up the whole PDU.
794      */
795
796     if (*parms->wholeMsgLen < parms->globalDataLen) {
797         DEBUGMSGTL(("ksm", "Building global data failed.\n"));
798         retval = SNMPERR_TOO_LONG;
799         goto error;
800     }
801
802     *offset += parms->globalDataLen;
803     memcpy(*wholeMsg + *parms->wholeMsgLen - *offset,
804            parms->globalData, parms->globalDataLen);
805
806     rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
807                                            offset, 1,
808                                            (u_char) (ASN_SEQUENCE |
809                                                      ASN_CONSTRUCTOR),
810                                            *offset);
811
812     if (rc == 0) {
813         DEBUGMSGTL(("ksm", "Building master packet sequence.\n"));
814         retval = SNMPERR_TOO_LONG;
815         goto error;
816     }
817
818     DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n"));
819
820     /*
821      * Now we need to checksum the entire PDU (since it's built).
822      */
823
824     pdu_checksum.contents = malloc(pdu_checksum.length);
825
826     if (!pdu_checksum.contents) {
827         DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n",
828                     pdu_checksum.length));
829         retval = SNMPERR_MALLOC;
830         goto error;
831     }
832
833     /*
834      * If we didn't encrypt the packet, we haven't yet got the subkey.
835      * Get that now.
836      */
837
838     if (!subkey) {
839         if (ksm_state)
840             retcode = krb5_auth_con_getremotesubkey(kcontext, auth_context,
841                                                     &subkey);
842         else
843             retcode = krb5_auth_con_getlocalsubkey(kcontext, auth_context,
844                                                    &subkey);
845         if (retcode) {
846             DEBUGMSGTL(("ksm", "krb5_auth_con_getlocalsubkey failed: %s\n",
847                         error_message(retcode)));
848             snmp_set_detail(error_message(retcode));
849             retval = SNMPERR_KRB5;
850             goto error;
851         }
852     }
853 #ifdef MIT_NEW_CRYPTO
854
855     input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset);
856     input.length = *offset;
857         retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey,
858                                        KSM_KEY_USAGE_CHECKSUM, &input,
859                                        &pdu_checksum);
860
861 #else                           /* MIT_NEW_CRYPTO */
862
863     retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg +
864                                       *parms->wholeMsgLen - *offset,
865                                       *offset,
866                                       (krb5_pointer) subkey->contents,
867                                       subkey->length, &pdu_checksum);
868
869 #endif                          /* MIT_NEW_CRYPTO */
870
871     if (retcode) {
872         DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n",
873                     error_message(retcode)));
874         retval = SNMPERR_KRB5;
875         snmp_set_detail(error_message(retcode));
876         goto error;
877     }
878
879     DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n"));
880
881     memcpy(cksum_pointer, pdu_checksum.contents, pdu_checksum.length);
882
883     DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n",
884                 pdu_checksum.length, cksum_pointer - (*wholeMsg + 1)));
885
886     DEBUGMSGTL(("ksm", "KSM: Checksum:"));
887
888     for (i = 0; i < pdu_checksum.length; i++)
889         DEBUGMSG(("ksm", " %02x",
890                   (unsigned int) pdu_checksum.contents[i]));
891
892     DEBUGMSG(("ksm", "\n"));
893
894     /*
895      * If we're _not_ called as part of a response (null ksm_state),
896      * then save the auth_context for later using our cache routines.
897      */
898
899     if (!ksm_state) {
900         if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context,
901                                        (u_char *) parms->secName,
902                                        parms->secNameLen)) !=
903             SNMPERR_SUCCESS)
904             goto error;
905         auth_context = NULL;
906     }
907
908     DEBUGMSGTL(("ksm", "KSM processing complete!\n"));
909
910   error:
911
912     if (pdu_checksum.contents)
913 #ifdef MIT_NEW_CRYPTO
914         krb5_free_checksum_contents(kcontext, &pdu_checksum);
915 #else                           /* MIT_NEW_CRYPTO */
916         free(pdu_checksum.contents);
917 #endif                          /* MIT_NEW_CRYPTO */
918
919     if (ivector.data)
920         free(ivector.data);
921
922     if (subkey)
923         krb5_free_keyblock(kcontext, subkey);
924
925     if (encrypted_data)
926         free(encrypted_data);
927
928     if (cc)
929         krb5_cc_close(kcontext, cc);
930
931     if (auth_context && !ksm_state)
932         krb5_auth_con_free(kcontext, auth_context);
933
934     return retval;
935 }
936
937 /****************************************************************************
938  *
939  * ksm_process_in_msg
940  *
941  * Parameters:
942  *      (See list below...)
943  *
944  * Returns:
945  *      KSM_ERR_NO_ERROR                        On success.
946  *      SNMPERR_KRB5
947  *      KSM_ERR_GENERIC_ERROR
948  *      KSM_ERR_UNSUPPORTED_SECURITY_LEVEL
949  *
950  *
951  * Processes an incoming message.
952  *
953  ****************************************************************************/
954
955 int
956 ksm_process_in_msg(struct snmp_secmod_incoming_params *parms)
957 {
958     long            temp;
959     krb5_cksumtype  cksumtype;
960     krb5_auth_context auth_context = NULL;
961     krb5_error_code retcode;
962     krb5_checksum   checksum;
963     krb5_data       ap_req, ivector;
964     krb5_flags      flags;
965     krb5_keyblock  *subkey = NULL;
966 #ifdef MIT_NEW_CRYPTO
967     krb5_data       input, output;
968     krb5_boolean    valid;
969     krb5_enc_data   in_crypt;
970 #else                           /* MIT_NEW_CRYPTO */
971     krb5_encrypt_block eblock;
972 #endif                          /* MIT_NEW_CRYPTO */
973     krb5_ticket    *ticket = NULL;
974     int             retval = SNMPERR_SUCCESS, response = 0;
975     size_t          length =
976         parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg);
977     u_char         *current = parms->secParams, type;
978     size_t          cksumlength, blocksize;
979     long            hint;
980     char           *cname;
981     struct ksm_secStateRef *ksm_state;
982     struct ksm_cache_entry *entry;
983
984     DEBUGMSGTL(("ksm", "Processing has begun\n"));
985
986     checksum.contents = NULL;
987     ap_req.data = NULL;
988     ivector.length = 0;
989     ivector.data = NULL;
990
991     /*
992      * First, parse the security parameters (because we need the subkey inside
993      * of the ticket to do anything
994      */
995
996     if ((current = asn_parse_sequence(current, &length, &type,
997                                       (ASN_UNIVERSAL | ASN_PRIMITIVE |
998                                        ASN_OCTET_STR),
999                                       "ksm first octet")) == NULL) {
1000         DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n"));
1001
1002         retval = SNMPERR_ASN_PARSE_ERR;
1003         goto error;
1004     }
1005
1006     if ((current = asn_parse_sequence(current, &length, &type,
1007                                       (ASN_SEQUENCE | ASN_CONSTRUCTOR),
1008                                       "ksm sequence")) == NULL) {
1009         DEBUGMSGTL(("ksm",
1010                     "Security parameter sequence parsing failed\n"));
1011
1012         retval = SNMPERR_ASN_PARSE_ERR;
1013         goto error;
1014     }
1015
1016     if ((current = asn_parse_int(current, &length, &type, &temp,
1017                                  sizeof(temp))) == NULL) {
1018         DEBUGMSGTL(("ksm", "Security parameter checksum type parsing"
1019                     "failed\n"));
1020
1021         retval = SNMPERR_ASN_PARSE_ERR;
1022         goto error;
1023     }
1024
1025     cksumtype = temp;
1026
1027     if (!valid_cksumtype(cksumtype)) {
1028         DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));
1029
1030         retval = SNMPERR_KRB5;
1031         snmp_set_detail("Invalid checksum type");
1032         goto error;
1033     }
1034
1035     if (!is_keyed_cksum(cksumtype)) {
1036         DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
1037                     cksumtype));
1038         snmp_set_detail("Checksum is not a keyed checksum");
1039         retval = SNMPERR_KRB5;
1040         goto error;
1041     }
1042
1043     if (!is_coll_proof_cksum(cksumtype)) {
1044         DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
1045                     "checksum\n", cksumtype));
1046         snmp_set_detail("Checksum is not a collision-proof checksum");
1047         retval = SNMPERR_KRB5;
1048         goto error;
1049     }
1050
1051     checksum.checksum_type = cksumtype;
1052
1053     cksumlength = length;
1054
1055     if ((current = asn_parse_sequence(current, &cksumlength, &type,
1056                                       (ASN_UNIVERSAL | ASN_PRIMITIVE |
1057                                        ASN_OCTET_STR), "ksm checksum")) ==
1058         NULL) {
1059         DEBUGMSGTL(("ksm",
1060                     "Security parameter checksum parsing failed\n"));
1061
1062         retval = SNMPERR_ASN_PARSE_ERR;
1063         goto error;
1064     }
1065
1066     checksum.contents = malloc(cksumlength);
1067     if (!checksum.contents) {
1068         DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n",
1069                     cksumlength));
1070         retval = SNMPERR_MALLOC;
1071         goto error;
1072     }
1073
1074     memcpy(checksum.contents, current, cksumlength);
1075
1076     checksum.length = cksumlength;
1077     checksum.checksum_type = cksumtype;
1078
1079     /*
1080      * Zero out the checksum so the validation works correctly
1081      */
1082
1083     memset(current, 0, cksumlength);
1084
1085     current += cksumlength;
1086     length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);
1087
1088     if ((current = asn_parse_sequence(current, &length, &type,
1089                                       (ASN_UNIVERSAL | ASN_PRIMITIVE |
1090                                        ASN_OCTET_STR), "ksm ap_req")) ==
1091         NULL) {
1092         DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing "
1093                     "failed\n"));
1094
1095         retval = SNMPERR_ASN_PARSE_ERR;
1096         goto error;
1097     }
1098
1099     ap_req.length = length;
1100     ap_req.data = malloc(length);
1101     if (!ap_req.data) {
1102         DEBUGMSGTL(("ksm",
1103                     "KSM unable to malloc %d bytes for AP_REQ/REP.\n",
1104                     length));
1105         retval = SNMPERR_MALLOC;
1106         goto error;
1107     }
1108
1109     memcpy(ap_req.data, current, length);
1110
1111     current += length;
1112     length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);
1113
1114     if ((current = asn_parse_int(current, &length, &type, &hint,
1115                                  sizeof(hint))) == NULL) {
1116         DEBUGMSGTL(("ksm",
1117                     "KSM security parameter hint parsing failed\n"));
1118
1119         retval = SNMPERR_ASN_PARSE_ERR;
1120         goto error;
1121     }
1122
1123     /*
1124      * Okay!  We've got it all!  Now try decoding the damn ticket.
1125      *
1126      * But of course there's a WRINKLE!  We need to figure out if we're
1127      * processing a AP_REQ or an AP_REP.  How do we do that?  We're going
1128      * to cheat, and look at the first couple of bytes (which is what
1129      * the Kerberos library routines do anyway).
1130      *
1131      * If there are ever new Kerberos message formats, we'll need to fix
1132      * this here.
1133      *
1134      * If it's a _response_, then we need to get the auth_context
1135      * from our cache.
1136      */
1137
1138     if (ap_req.length
1139         && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) {
1140
1141         /*
1142          * We need to initalize the authorization context, and set the
1143          * replay cache in it (and initialize the replay cache if we
1144          * haven't already
1145          */
1146
1147         retcode = krb5_auth_con_init(kcontext, &auth_context);
1148
1149         if (retcode) {
1150             DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n",
1151                         error_message(retcode)));
1152             retval = SNMPERR_KRB5;
1153             snmp_set_detail(error_message(retcode));
1154             goto error;
1155         }
1156
1157         if (!rcache) {
1158             krb5_data       server;
1159             server.data = "host";
1160             server.length = strlen(server.data);
1161
1162             retcode = krb5_get_server_rcache(kcontext, &server, &rcache);
1163
1164             if (retcode) {
1165                 DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n",
1166                             error_message(retcode)));
1167                 retval = SNMPERR_KRB5;
1168                 snmp_set_detail(error_message(retcode));
1169                 goto error;
1170             }
1171         }
1172
1173         retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache);
1174
1175         if (retcode) {
1176             DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n",
1177                         error_message(retcode)));
1178             retval = SNMPERR_KRB5;
1179             snmp_set_detail(error_message(retcode));
1180             goto error;
1181         }
1182
1183         retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL,
1184                               NULL, &flags, &ticket);
1185
1186         krb5_auth_con_setrcache(kcontext, auth_context, NULL);
1187
1188         if (retcode) {
1189             DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n",
1190                         error_message(retcode)));
1191             retval = SNMPERR_KRB5;
1192             snmp_set_detail(error_message(retcode));
1193             goto error;
1194         }
1195
1196         retcode =
1197             krb5_unparse_name(kcontext, ticket->enc_part2->client, &cname);
1198
1199         if (retcode == 0) {
1200             DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n",
1201                         cname));
1202             free(cname);
1203         }
1204
1205         /*
1206          * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set
1207          */
1208
1209         if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) {
1210             DEBUGMSGTL(("ksm",
1211                         "KSM MUTUAL_REQUIRED not set in request!\n"));
1212             retval = SNMPERR_KRB5;
1213             snmp_set_detail("MUTUAL_REQUIRED not set in message");
1214             goto error;
1215         }
1216
1217         retcode =
1218             krb5_auth_con_getremotesubkey(kcontext, auth_context, &subkey);
1219
1220         if (retcode) {
1221             DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n",
1222                         error_message(retcode)));
1223             retval = SNMPERR_KRB5;
1224             snmp_set_detail(error_message(retcode));
1225             goto error;
1226         }
1227
1228     } else if (ap_req.length && (ap_req.data[0] == 0x6f ||
1229                                  ap_req.data[0] == 0x4f)) {
1230         /*
1231          * Looks like a response; let's see if we've got that auth_context
1232          * in our cache.
1233          */
1234
1235         krb5_ap_rep_enc_part *repl = NULL;
1236
1237         response = 1;
1238
1239         entry = ksm_get_cache(parms->pdu->msgid);
1240
1241         if (!entry) {
1242             DEBUGMSGTL(("ksm",
1243                         "KSM: Unable to find auth_context for PDU with "
1244                         "message ID of %ld\n", parms->pdu->msgid));
1245             retval = SNMPERR_KRB5;
1246             goto error;
1247         }
1248
1249         auth_context = entry->auth_context;
1250
1251         /*
1252          * In that case, let's call the rd_rep function
1253          */
1254
1255         retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl);
1256
1257         if (repl)
1258             krb5_free_ap_rep_enc_part(kcontext, repl);
1259
1260         if (retcode) {
1261             DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n",
1262                         error_message(retcode)));
1263             retval = SNMPERR_KRB5;
1264             goto error;
1265         }
1266
1267         DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n"));
1268
1269         retcode =
1270             krb5_auth_con_getlocalsubkey(kcontext, auth_context, &subkey);
1271
1272         if (retcode) {
1273             DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n",
1274                         error_message(retcode)));
1275             retval = SNMPERR_KRB5;
1276             snmp_set_detail("Unable to retrieve local subkey");
1277             goto error;
1278         }
1279
1280     } else {
1281         DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n",
1282                     ap_req.data[0]));
1283         retval = SNMPERR_KRB5;
1284         snmp_set_detail("Unknown Kerberos message type");
1285         goto error;
1286     }
1287
1288 #ifdef MIT_NEW_CRYPTO
1289     input.data = (char *) parms->wholeMsg;
1290     input.length = parms->wholeMsgLen;
1291
1292     retcode =
1293         krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM,
1294                                &input, &checksum, &valid);
1295 #else                           /* MIT_NEW_CRYPTO */
1296     retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum,
1297                                    parms->wholeMsg, parms->wholeMsgLen,
1298                                    (krb5_pointer) subkey->contents,
1299                                    subkey->length);
1300 #endif                          /* MIT_NEW_CRYPTO */
1301
1302     if (retcode) {
1303         DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n",
1304                     error_message(retcode)));
1305         retval = SNMPERR_KRB5;
1306         snmp_set_detail(error_message(retcode));
1307         goto error;
1308     }
1309
1310     /*
1311      * Don't ask me why they didn't simply return an error, but we have
1312      * to check to see if "valid" is false.
1313      */
1314
1315 #ifdef MIT_NEW_CRYPTO
1316     if (!valid) {
1317         DEBUGMSGTL(("ksm", "Computed checksum did not match supplied "
1318                     "checksum!\n"));
1319         retval = SNMPERR_KRB5;
1320         snmp_set_detail
1321             ("Computed checksum did not match supplied checksum");
1322         goto error;
1323     }
1324 #endif                          /* MIT_NEW_CRYPTO */
1325
1326     /*
1327      * Handle an encrypted PDU.  Note that it's an OCTET_STRING of the
1328      * output of whatever Kerberos cryptosystem you're using (defined by
1329      * the encryption type).  Note that this is NOT the EncryptedData
1330      * sequence - it's what goes in the "cipher" field of EncryptedData.
1331      */
1332
1333     if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
1334
1335         if ((current = asn_parse_sequence(current, &length, &type,
1336                                           (ASN_UNIVERSAL | ASN_PRIMITIVE |
1337                                            ASN_OCTET_STR), "ksm pdu")) ==
1338             NULL) {
1339             DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n"));
1340             retval = SNMPERR_ASN_PARSE_ERR;
1341             goto error;
1342         }
1343
1344         /*
1345          * The PDU is now pointed at by "current", and the length is in
1346          * "length".
1347          */
1348
1349         DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n"));
1350
1351         /*
1352          * We need to set up a blank initialization vector for the decryption.
1353          * Use a block of all zero's (which is dependent on the block size
1354          * of the encryption method).
1355          */
1356
1357 #ifdef MIT_NEW_CRYPTO
1358
1359         retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);
1360
1361         if (retcode) {
1362             DEBUGMSGTL(("ksm",
1363                         "Unable to determine crypto block size: %s\n",
1364                         error_message(retcode)));
1365             snmp_set_detail(error_message(retcode));
1366             retval = SNMPERR_KRB5;
1367             goto error;
1368         }
1369 #else                           /* MIT_NEW_CRYPTO */
1370
1371         blocksize =
1372             krb5_enctype_array[subkey->enctype]->system->block_length;
1373
1374 #endif                          /* MIT_NEW_CRYPTO */
1375
1376         ivector.data = malloc(blocksize);
1377
1378         if (!ivector.data) {
1379             DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
1380                         blocksize));
1381             retval = SNMPERR_MALLOC;
1382             goto error;
1383         }
1384
1385         ivector.length = blocksize;
1386         memset(ivector.data, 0, blocksize);
1387
1388 #ifndef MIT_NEW_CRYPTO
1389
1390         krb5_use_enctype(kcontext, &eblock, subkey->enctype);
1391
1392         retcode = krb5_process_key(kcontext, &eblock, subkey);
1393
1394         if (retcode) {
1395             DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n",
1396                         error_message(retcode)));
1397             snmp_set_detail(error_message(retcode));
1398             retval = SNMPERR_KRB5;
1399             goto error;
1400         }
1401 #endif                          /* !MIT_NEW_CRYPTO */
1402
1403         if (length > *parms->scopedPduLen) {
1404             DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to "
1405                         "decrypt but only %d bytes available\n", length,
1406                         *parms->scopedPduLen));
1407             retval = SNMPERR_TOO_LONG;
1408 #ifndef MIT_NEW_CRYPTO
1409             krb5_finish_key(kcontext, &eblock);
1410 #endif                          /* ! MIT_NEW_CRYPTO */
1411             goto error;
1412         }
1413 #ifdef MIT_NEW_CRYPTO
1414         in_crypt.ciphertext.data = (char *) current;
1415         in_crypt.ciphertext.length = length;
1416         in_crypt.enctype = subkey->enctype;
1417         output.data = (char *) *parms->scopedPdu;
1418         output.length = *parms->scopedPduLen;
1419
1420         retcode =
1421             krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
1422                            &ivector, &in_crypt, &output);
1423 #else                           /* MIT_NEW_CRYPTO */
1424
1425         retcode = krb5_decrypt(kcontext, (krb5_pointer) current,
1426                                *parms->scopedPdu, length, &eblock,
1427                                ivector.data);
1428
1429         krb5_finish_key(kcontext, &eblock);
1430
1431 #endif                          /* MIT_NEW_CRYPTO */
1432
1433         if (retcode) {
1434             DEBUGMSGTL(("ksm", "Decryption failed: %s\n",
1435                         error_message(retcode)));
1436             snmp_set_detail(error_message(retcode));
1437             retval = SNMPERR_KRB5;
1438             goto error;
1439         }
1440
1441         *parms->scopedPduLen = length;
1442
1443     } else {
1444         /*
1445          * Clear PDU
1446          */
1447
1448         *parms->scopedPdu = current;
1449         *parms->scopedPduLen =
1450             parms->wholeMsgLen - (current - parms->wholeMsg);
1451     }
1452
1453     /*
1454      * A HUGE GROSS HACK
1455      */
1456
1457     *parms->maxSizeResponse = parms->maxMsgSize - 200;
1458
1459     DEBUGMSGTL(("ksm", "KSM processing complete\n"));
1460
1461     /*
1462      * Set the secName to the right value (a hack for now).  But that's
1463      * only used for when we're processing a request, not a response.
1464      */
1465
1466     if (!response) {
1467
1468         retcode = krb5_unparse_name(kcontext, ticket->enc_part2->client,
1469                                     &cname);
1470
1471         if (retcode) {
1472             DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n",
1473                         error_message(retcode)));
1474             snmp_set_detail(error_message(retcode));
1475             retval = SNMPERR_KRB5;
1476             goto error;
1477         }
1478
1479         if (strlen(cname) > *parms->secNameLen + 1) {
1480             DEBUGMSGTL(("ksm",
1481                         "KSM: Principal length (%d) is too long (%d)\n",
1482                         strlen(cname), parms->secNameLen));
1483             retval = SNMPERR_TOO_LONG;
1484             free(cname);
1485             goto error;
1486         }
1487
1488         strcpy(parms->secName, cname);
1489         *parms->secNameLen = strlen(cname);
1490
1491         free(cname);
1492
1493         /*
1494          * Also, if we're not a response, keep around our auth_context so we
1495          * can encode the reply message correctly
1496          */
1497
1498         ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef);
1499
1500         if (!ksm_state) {
1501             DEBUGMSGTL(("ksm", "KSM unable to malloc memory for "
1502                         "ksm_secStateRef\n"));
1503             retval = SNMPERR_MALLOC;
1504             goto error;
1505         }
1506
1507         ksm_state->auth_context = auth_context;
1508         auth_context = NULL;
1509         ksm_state->cksumtype = cksumtype;
1510
1511         *parms->secStateRef = ksm_state;
1512     } else {
1513
1514         /*
1515          * We _still_ have to set the secName in process_in_msg().  Do
1516          * that now with what we were passed in before (we cached it,
1517          * remember?)
1518          */
1519
1520         memcpy(parms->secName, entry->secName, entry->secNameLen);
1521         *parms->secNameLen = entry->secNameLen;
1522     }
1523
1524     /*
1525      * Just in case
1526      */
1527
1528     parms->secEngineID = (u_char *) "";
1529     *parms->secEngineIDLen = 0;
1530
1531     auth_context = NULL;        /* So we don't try to free it on success */
1532
1533   error:
1534     if (retval == SNMPERR_ASN_PARSE_ERR &&
1535         snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0)
1536         DEBUGMSGTL(("ksm", "Failed to increment statistics.\n"));
1537
1538     if (subkey)
1539         krb5_free_keyblock(kcontext, subkey);
1540
1541     if (checksum.contents)
1542         free(checksum.contents);
1543
1544     if (ivector.data)
1545         free(ivector.data);
1546
1547     if (ticket)
1548         krb5_free_ticket(kcontext, ticket);
1549
1550     if (!response && auth_context)
1551         krb5_auth_con_free(kcontext, auth_context);
1552
1553     if (ap_req.data)
1554         free(ap_req.data);
1555
1556     return retval;
1557 }