Coverage Report

Created: 2026-10-17 06:54

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/net-snmp/snmplib/snmpksm.c
Line
Count
Source
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
#include <stdio.h>
13
#ifdef HAVE_STDLIB_H
14
#include <stdlib.h>
15
#endif
16
#ifdef TIME_WITH_SYS_TIME
17
# include <sys/time.h>
18
# include <time.h>
19
#else
20
# ifdef HAVE_SYS_TIME_H
21
#  include <sys/time.h>
22
# else
23
#  include <time.h>
24
# endif
25
#endif
26
#ifdef HAVE_STRING_H
27
#include <string.h>
28
#else
29
#include <strings.h>
30
#endif
31
#ifdef HAVE_NETINET_IN_H
32
#include <netinet/in.h>
33
#endif
34
#include <errno.h>
35
36
37
#ifdef NETSNMP_USE_KERBEROS_HEIMDAL
38
#ifndef NETSNMP_USE_KERBEROS_MIT
39
#define OLD_HEIMDAL
40
#endif        /* ! NETSNMP_USE_KERBEROS_MIT */
41
#else
42
#define KRB5_DEPRECATED 1
43
#endif        /* NETSNMP_USE_KERBEROS_HEIMDAL */
44
45
#ifdef NETSNMP_USE_KERBEROS_HEIMDAL
46
#define oid heimdal_oid_renamed
47
#endif        /* NETSNMP_USE_KERBEROS_HEIMDAL */
48
#include <krb5.h>
49
#ifdef NETSNMP_USE_KERBEROS_HEIMDAL
50
#undef oid
51
#endif        /* NETSNMP_USE_KERBEROS_HEIMDAL */
52
53
#ifdef NETSNMP_USE_KERBEROS_HEIMDAL
54
#define CHECKSUM_TYPE(x)  (x)->cksumtype
55
#define CHECKSUM_CONTENTS(x)  (x)->checksum.data
56
#define CHECKSUM_LENGTH(x)  (x)->checksum.length
57
#define TICKET_CLIENT(x)  (x)->client
58
#else       /* NETSNMP_USE_KERBEROS_HEIMDAL */
59
0
#define CHECKSUM_TYPE(x)  (x)->checksum_type
60
0
#define CHECKSUM_CONTENTS(x)  (x)->contents
61
0
#define CHECKSUM_LENGTH(x)  (x)->length
62
0
#define TICKET_CLIENT(x)  (x)->enc_part2->client
63
#endif        /* NETSNMP_USE_KERBEROS_HEIMDAL */
64
65
#ifdef HAVE_ET_COM_ERR_H
66
#include <et/com_err.h>
67
#elif defined(HAVE_COM_ERR_H)
68
#include <com_err.h>
69
#else
70
static const char *error_message(int ret) { return "(?)"; }
71
#endif
72
73
#include <net-snmp/output_api.h>
74
#include <net-snmp/config_api.h>
75
#include <net-snmp/utilities.h>
76
77
#include <net-snmp/library/asn1.h>
78
#include <net-snmp/library/snmp_api.h>
79
#include <net-snmp/library/callback.h>
80
#include <net-snmp/library/keytools.h>
81
#include <net-snmp/library/snmpv3.h>
82
#include <net-snmp/library/lcd_time.h>
83
#include <net-snmp/library/scapi.h>
84
#include <net-snmp/library/callback.h>
85
#include <net-snmp/library/snmp_secmod.h>
86
#include <net-snmp/library/snmpksm.h>
87
88
static krb5_context kcontext = NULL;
89
static krb5_rcache rcache = NULL;
90
static krb5_keytab keytab = NULL;
91
static int keytab_setup = 0;
92
static char *service_name = NULL;
93
static char service_host[] = "host";
94
static u_char null_id[] = {0};
95
96
static int      ksm_session_init(netsnmp_session *);
97
static void     ksm_free_state_ref(void *);
98
static int      ksm_free_pdu(netsnmp_pdu *);
99
static int      ksm_clone_pdu(netsnmp_pdu *, netsnmp_pdu *);
100
101
static int      ksm_insert_cache(long, krb5_auth_context, u_char *,
102
                                 size_t);
103
static void     ksm_decrement_ref_count(long);
104
static void     ksm_increment_ref_count(long);
105
static struct ksm_cache_entry *ksm_get_cache(long);
106
107
#if !defined(HAVE_KRB5_AUTH_CON_GETSENDSUBKEY) /* Heimdal */
108
109
krb5_error_code krb5_auth_con_getsendsubkey(krb5_context context,
110
        krb5_auth_context auth_context, 
111
        krb5_keyblock **keyblock)
112
{
113
    return krb5_auth_con_getlocalsubkey(context, auth_context, keyblock);
114
}
115
116
#endif
117
118
#if !defined(HAVE_KRB5_AUTH_CON_GETRECVSUBKEY) /* Heimdal */
119
120
krb5_error_code krb5_auth_con_getrecvsubkey(krb5_context context,
121
        krb5_auth_context auth_context, 
122
        krb5_keyblock **keyblock)
123
{
124
    return krb5_auth_con_getremotesubkey(context, auth_context, keyblock);
125
}
126
127
#endif
128
129
0
#define HASHSIZE  64
130
131
/*
132
 * Our information stored for the response PDU.
133
 */
134
135
struct ksm_secStateRef {
136
    krb5_auth_context auth_context;
137
    krb5_cksumtype  cksumtype;
138
};
139
140
/*
141
 * A KSM outgoing pdu cache entry
142
 */
143
144
struct ksm_cache_entry {
145
    long            msgid;
146
    int             refcount;
147
    krb5_auth_context auth_context;
148
    u_char         *secName;
149
    size_t          secNameLen;
150
    struct ksm_cache_entry *next;
151
};
152
153
/*
154
 * Poor man's hash table
155
 */
156
157
static struct ksm_cache_entry *ksm_hash_table[HASHSIZE];
158
159
/*
160
 * Stuff to deal with config values
161
 * Note the conditionals that wrap these--i don't know if these are
162
 * needed, since i don't know how library initialization and callbacks
163
 * and stuff work
164
 */
165
166
static int
167
init_snmpksm_post_config(int majorid, int minorid, void *serverarg,
168
       void *clientarg)
169
0
{
170
171
0
    if (kcontext == NULL) {
172
  /* not reached, I'd imagine */
173
0
        return SNMPERR_KRB5;
174
0
    }
175
176
0
    if (service_name == NULL) {
177
  /* always reached, I'd imagine */
178
0
  char *c = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
179
0
          NETSNMP_DS_LIB_KSM_SERVICE_NAME);
180
0
  if (c != NULL) {
181
0
    service_name = c;
182
0
  }
183
0
  else {
184
0
    service_name = service_host;
185
0
  }
186
0
    }
187
188
0
    if (keytab_setup == 0) {
189
  /* always reached, I'd imagine */
190
0
  char *c = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID,
191
0
          NETSNMP_DS_LIB_KSM_KEYTAB);
192
0
  if (c) {
193
0
      krb5_error_code retval;
194
0
      DEBUGMSGTL(("ksm", "Using keytab %s\n", c));
195
0
      retval = krb5_kt_resolve(kcontext, c, &keytab);
196
0
      if (retval) {
197
0
    DEBUGMSGTL(("ksm", "krb5_kt_resolve(\"%s\") failed. KSM "
198
0
          "config callback failing\n", error_message(retval)));
199
0
    return SNMPERR_KRB5;
200
0
      }
201
0
  }
202
0
  else {
203
0
      DEBUGMSGTL(("ksm", "Using default keytab\n"));
204
0
  }
205
0
  keytab_setup = 1;
206
0
    }
207
208
0
    return SNMPERR_SUCCESS;
209
0
}
210
211
/*
212
 * Initialize all of the state required for Kerberos (right now, just call
213
 * krb5_init_context).
214
 */
215
216
void
217
init_ksm(void)
218
0
{
219
0
    krb5_error_code retval;
220
0
    struct snmp_secmod_def *def;
221
0
    int             i;
222
223
0
    netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defKSMKeytab",
224
0
                               NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_KSM_KEYTAB);
225
0
    netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "defKSMServiceName",
226
0
                               NETSNMP_DS_LIBRARY_ID,
227
0
             NETSNMP_DS_LIB_KSM_SERVICE_NAME);
228
0
    snmp_register_callback(SNMP_CALLBACK_LIBRARY,
229
0
         SNMP_CALLBACK_POST_READ_CONFIG,
230
0
         init_snmpksm_post_config, NULL);
231
232
233
0
    if (kcontext == NULL) {
234
0
        retval = krb5_init_context(&kcontext);
235
236
0
        if (retval) {
237
0
            DEBUGMSGTL(("ksm", "krb5_init_context failed (%s), not "
238
0
                        "registering KSM\n", error_message(retval)));
239
0
            return;
240
0
        }
241
0
    }
242
243
0
    for (i = 0; i < HASHSIZE; i++)
244
0
        ksm_hash_table[i] = NULL;
245
246
0
    def = SNMP_MALLOC_STRUCT(snmp_secmod_def);
247
248
0
    if (!def) {
249
0
        DEBUGMSGTL(("ksm", "Unable to malloc snmp_secmod struct, not "
250
0
                    "registering KSM\n"));
251
0
        return;
252
0
    }
253
254
0
    def->encode_reverse = ksm_rgenerate_out_msg;
255
0
    def->decode = ksm_process_in_msg;
256
0
    def->session_open = ksm_session_init;
257
0
    def->pdu_free_state_ref = ksm_free_state_ref;
258
0
    def->pdu_free = ksm_free_pdu;
259
0
    def->pdu_clone = ksm_clone_pdu;
260
261
0
    register_sec_mod(NETSNMP_SEC_MODEL_KSM, "ksm", def);
262
0
}
263
264
void shutdown_ksm(void)
265
0
{
266
0
    unregister_sec_mod(NETSNMP_SEC_MODEL_KSM);
267
0
    if (kcontext)
268
0
        krb5_free_context(kcontext);
269
0
    kcontext = NULL;
270
0
}
271
272
/*
273
 * These routines implement a simple cache for information we need to
274
 * process responses.  When we send out a request, it contains an AP_REQ;
275
 * we get back an AP_REP, and we need the authorization context from the
276
 * AP_REQ to decrypt the AP_REP.  But because right now there's nothing
277
 * that gets preserved across calls to rgenerate_out_msg to process_in_msg,
278
 * we cache these internally based on the message ID (we also cache the
279
 * passed-in security name, for reasons that are mostly stupid).
280
 */
281
282
static int
283
ksm_insert_cache(long msgid, krb5_auth_context auth_context,
284
                 u_char * secName, size_t secNameLen)
285
0
{
286
0
    struct ksm_cache_entry *entry;
287
0
    int             bucket;
288
289
0
    entry = SNMP_MALLOC_STRUCT(ksm_cache_entry);
290
0
    if (!entry)
291
0
        return SNMPERR_MALLOC;
292
293
0
    entry->msgid = msgid;
294
0
    entry->auth_context = auth_context;
295
0
    entry->refcount = 1;
296
0
    entry->secName = netsnmp_memdup(secName, secNameLen);
297
0
    if (secName && !entry->secName) {
298
0
        free(entry);
299
0
        return SNMPERR_GENERR;
300
0
    }
301
302
0
    entry->secNameLen = secNameLen;
303
304
0
    bucket = (unsigned long)msgid % HASHSIZE;
305
306
0
    entry->next = ksm_hash_table[bucket];
307
0
    ksm_hash_table[bucket] = entry;
308
309
0
    return SNMPERR_SUCCESS;
310
0
}
311
312
static struct ksm_cache_entry *
313
ksm_get_cache(long msgid)
314
0
{
315
0
    struct ksm_cache_entry *entry;
316
0
    int             bucket;
317
318
0
    bucket = (unsigned long)msgid % HASHSIZE;
319
320
0
    for (entry = ksm_hash_table[bucket]; entry != NULL;
321
0
         entry = entry->next)
322
0
        if (entry->msgid == msgid)
323
0
            return entry;
324
325
0
    return NULL;
326
0
}
327
328
static void
329
ksm_decrement_ref_count(long msgid)
330
0
{
331
0
    struct ksm_cache_entry *entry, *entry1;
332
0
    int             bucket;
333
334
0
    bucket = (unsigned long)msgid % HASHSIZE;
335
336
0
    if (ksm_hash_table[bucket] && ksm_hash_table[bucket]->msgid == msgid) {
337
0
        entry = ksm_hash_table[bucket];
338
339
        /*
340
         * If the reference count is zero, then free it
341
         */
342
343
0
        if (--entry->refcount <= 0) {
344
0
            DEBUGMSGTL(("ksm", "Freeing entry for msgid %ld\n", msgid));
345
0
            krb5_auth_con_free(kcontext, entry->auth_context);
346
0
            free(entry->secName);
347
0
            ksm_hash_table[bucket] = entry->next;
348
0
            free(entry);
349
0
        }
350
351
0
        return;
352
353
0
    } else if (ksm_hash_table[bucket])
354
0
        for (entry1 = ksm_hash_table[bucket], entry = entry1->next;
355
0
             entry != NULL; entry1 = entry, entry = entry->next)
356
0
            if (entry->msgid == msgid) {
357
358
0
                if (--entry->refcount <= 0) {
359
0
                    DEBUGMSGTL(("ksm", "Freeing entry for msgid %ld\n",
360
0
                                msgid));
361
0
                    krb5_auth_con_free(kcontext, entry->auth_context);
362
0
                    free(entry->secName);
363
0
                    entry1->next = entry->next;
364
0
                    free(entry);
365
0
                }
366
367
0
                return;
368
0
            }
369
370
0
    DEBUGMSGTL(("ksm",
371
0
                "KSM: Unable to decrement cache entry for msgid %ld.\n",
372
0
                msgid));
373
0
}
374
375
static void
376
ksm_increment_ref_count(long msgid)
377
0
{
378
0
    struct ksm_cache_entry *entry = ksm_get_cache(msgid);
379
380
0
    if (!entry) {
381
0
        DEBUGMSGTL(("ksm", "Unable to find cache entry for msgid %ld "
382
0
                    "for increment\n", msgid));
383
0
        return;
384
0
    }
385
386
0
    entry->refcount++;
387
0
}
388
389
/*
390
 * Initialize specific session information (right now, just set up things to
391
 * not do an engineID probe)
392
 */
393
394
static int
395
ksm_session_init(netsnmp_session * sess)
396
0
{
397
0
    DEBUGMSGTL(("ksm",
398
0
                "KSM: Reached our session initialization callback\n"));
399
400
0
    sess->flags |= SNMP_FLAGS_DONT_PROBE;
401
402
0
    return SNMPERR_SUCCESS;
403
0
}
404
405
/*
406
 * Free our state information (this is only done on the agent side)
407
 */
408
409
static void
410
ksm_free_state_ref(void *ptr)
411
0
{
412
0
    struct ksm_secStateRef *ref = (struct ksm_secStateRef *) ptr;
413
414
0
    DEBUGMSGTL(("ksm", "KSM: Freeing state reference\n"));
415
416
0
    krb5_auth_con_free(kcontext, ref->auth_context);
417
418
0
    free(ref);
419
0
}
420
421
/*
422
 * This is called when the PDU is freed; this will decrement reference counts
423
 * for entries in our state cache.
424
 */
425
426
static int
427
ksm_free_pdu(netsnmp_pdu *pdu)
428
0
{
429
0
    ksm_decrement_ref_count(pdu->msgid);
430
431
0
    DEBUGMSGTL(("ksm", "Decrementing cache entry for PDU msgid %ld\n",
432
0
                pdu->msgid));
433
434
0
    return SNMPERR_SUCCESS;
435
0
}
436
437
/*
438
 * This is called when a PDU is cloned (to increase reference counts)
439
 */
440
441
static int
442
ksm_clone_pdu(netsnmp_pdu *pdu, netsnmp_pdu *pdu2)
443
0
{
444
0
    ksm_increment_ref_count(pdu->msgid);
445
446
0
    DEBUGMSGTL(("ksm", "Incrementing cache entry for PDU msgid %ld\n",
447
0
                pdu->msgid));
448
449
0
    return SNMPERR_SUCCESS;
450
0
}
451
452
/****************************************************************************
453
 *
454
 * ksm_generate_out_msg
455
 *
456
 * Parameters:
457
 *  (See list below...)
458
 *
459
 * Returns:
460
 *  SNMPERR_GENERIC                        On success.
461
 *  SNMPERR_KRB5
462
 *  ... and others
463
 *
464
 *
465
 * Generate an outgoing message.
466
 *
467
 ****************************************************************************/
468
469
int
470
ksm_rgenerate_out_msg(struct snmp_secmod_outgoing_params *parms)
471
0
{
472
0
    krb5_auth_context auth_context = NULL;
473
0
    krb5_error_code retcode;
474
0
    krb5_ccache     cc = NULL;
475
0
    int             retval = SNMPERR_SUCCESS;
476
0
    krb5_data       outdata, ivector;
477
0
    krb5_keyblock  *subkey = NULL;
478
0
#ifdef NETSNMP_USE_KERBEROS_MIT
479
0
    krb5_data       input;
480
0
    krb5_enc_data   output;
481
0
    unsigned int    numcksumtypes;
482
0
    krb5_cksumtype  *cksumtype_array;
483
#elif defined OLD_HEIMDAL /* NETSNMP_USE_KERBEROS_MIT */
484
    krb5_crypto heim_crypto = NULL;
485
#else                           /* NETSNMP_USE_KERBEROS_MIT */
486
    krb5_encrypt_block eblock;
487
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
488
0
#ifndef OLD_HEIMDAL
489
0
    size_t          blocksize;
490
0
#endif
491
0
    size_t          encrypted_length;
492
0
    unsigned char  *encrypted_data = NULL;
493
0
    long            zero = 0, tmp;
494
0
    int             i;
495
0
    size_t          cksum_offset;
496
0
    krb5_cksumtype  cksumtype;
497
0
    krb5_checksum   pdu_checksum;
498
0
    u_char         **wholeMsg = parms->wholeMsg;
499
0
    size_t     *offset = parms->wholeMsgOffset, seq_offset;
500
0
    struct ksm_secStateRef *ksm_state = (struct ksm_secStateRef *)
501
0
        parms->secStateRef;
502
#ifdef OLD_HEIMDAL
503
    krb5_data encrypted_scoped_pdu;
504
#endif        /* OLD_HEIMDAL */
505
0
    int rc;
506
0
    char *colon = NULL;
507
508
0
    DEBUGMSGTL(("ksm", "Starting KSM processing\n"));
509
510
0
    outdata.length = 0;
511
0
    outdata.data = NULL;
512
0
    ivector.length = 0;
513
0
    ivector.data = NULL;
514
0
    CHECKSUM_CONTENTS(&pdu_checksum) = NULL;
515
516
0
    if (!ksm_state) {
517
        /*
518
         * If we've got a port number as part of the "peername", then
519
         * suppress this (temporarily) while we build the credential info.
520
         *   XXX - what about "udp:host" style addresses?
521
         */
522
0
        colon = strrchr(parms->session->peername, ':');
523
0
        if (colon != NULL) {
524
0
            *colon='\0';
525
0
        }
526
527
        /*
528
         * If we don't have a ksm_state, then we're a request.  Get a
529
         * credential cache and build a ap_req.
530
         */
531
0
        retcode = krb5_cc_default(kcontext, &cc);
532
533
0
        if (retcode) {
534
0
            DEBUGMSGTL(("ksm", "KSM: krb5_cc_default failed: %s\n",
535
0
                        error_message(retcode)));
536
0
            snmp_set_detail(error_message(retcode));
537
0
            retval = SNMPERR_KRB5;
538
0
            goto error;
539
0
        }
540
541
0
        DEBUGMSGTL(("ksm", "KSM: Set credential cache successfully\n"));
542
543
        /*
544
         * This seems odd, since we don't need this until later (or earlier,
545
         * depending on how you look at it), but because the most likely
546
         * errors are Kerberos at this point, I'll get this now to save
547
         * time not encoding the rest of the packet.
548
         *
549
         * Also, we need the subkey to encrypt the PDU (if required).
550
         */
551
552
0
        retcode =
553
0
            krb5_mk_req(kcontext, &auth_context,
554
0
                        AP_OPTS_MUTUAL_REQUIRED | AP_OPTS_USE_SUBKEY,
555
0
                        service_name, parms->session->peername, NULL,
556
0
                        cc, &outdata);
557
558
0
        if (colon != NULL)
559
0
            *colon=':';
560
561
0
        if (retcode) {
562
0
            DEBUGMSGTL(("ksm", "KSM: krb5_mk_req failed: %s\n",
563
0
                        error_message(retcode)));
564
0
            snmp_set_detail(error_message(retcode));
565
0
            retval = SNMPERR_KRB5;
566
0
            goto error;
567
0
        }
568
569
0
  DEBUGMSGTL(("ksm", "KSM: ticket retrieved successfully for \"%s/%s\" "
570
0
        "(may not be actual ticket sname)\n", service_name,
571
0
        parms->session->peername));
572
573
0
    } else {
574
575
        /*
576
         * Grab the auth_context from our security state reference
577
         */
578
579
0
        auth_context = ksm_state->auth_context;
580
581
        /*
582
         * Bundle up an AP_REP.  Note that we do this only when we
583
         * have a security state reference (which means we're in an agent
584
         * and we're sending a response).
585
         */
586
587
0
        DEBUGMSGTL(("ksm", "KSM: Starting reply processing.\n"));
588
589
0
        retcode = krb5_mk_rep(kcontext, auth_context, &outdata);
590
591
0
        if (retcode) {
592
0
            DEBUGMSGTL(("ksm", "KSM: krb5_mk_rep failed: %s\n",
593
0
                        error_message(retcode)));
594
0
            snmp_set_detail(error_message(retcode));
595
0
            retval = SNMPERR_KRB5;
596
0
            goto error;
597
0
        }
598
599
0
        DEBUGMSGTL(("ksm", "KSM: Finished with krb5_mk_rep()\n"));
600
0
    }
601
602
    /*
603
     * If we have to encrypt the PDU, do that now
604
     */
605
606
0
    if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
607
608
0
        DEBUGMSGTL(("ksm", "KSM: Starting PDU encryption.\n"));
609
610
        /*
611
         * It's weird -
612
         *
613
         * If we're on the manager, it's a local subkey (because that's in
614
         * our AP_REQ)
615
         *
616
         * If we're on the agent, it's a remote subkey (because that comes
617
         * FROM the received AP_REQ).
618
         */
619
620
0
        if (ksm_state)
621
0
            retcode = krb5_auth_con_getrecvsubkey(kcontext, auth_context,
622
0
                                                    &subkey);
623
0
        else
624
0
            retcode = krb5_auth_con_getsendsubkey(kcontext, auth_context,
625
0
                                                   &subkey);
626
627
0
        if (retcode) {
628
0
            DEBUGMSGTL(("ksm",
629
0
                        "KSM: krb5_auth_con_getsendsubkey failed: %s\n",
630
0
                        error_message(retcode)));
631
0
            snmp_set_detail(error_message(retcode));
632
0
            retval = SNMPERR_KRB5;
633
0
            goto error;
634
0
        }
635
636
        /*
637
         * Note that here we need to handle different things between the
638
         * old and new crypto APIs.  First, we need to get the final encrypted
639
         * length of the PDU.
640
         */
641
642
0
#ifdef NETSNMP_USE_KERBEROS_MIT
643
0
        retcode = krb5_c_encrypt_length(kcontext, subkey->enctype,
644
0
                                        parms->scopedPduLen,
645
0
                                        &encrypted_length);
646
647
0
        if (retcode) {
648
0
            DEBUGMSGTL(("ksm",
649
0
                        "Encryption length calculation failed: %s\n",
650
0
                        error_message(retcode)));
651
0
            snmp_set_detail(error_message(retcode));
652
0
            retval = SNMPERR_KRB5;
653
0
            goto error;
654
0
        }
655
#elif defined OLD_HEIMDAL
656
  retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
657
        if (retcode) {
658
            DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n",
659
                        error_message(retcode)));
660
            snmp_set_detail(error_message(retcode));
661
            retval = SNMPERR_KRB5;
662
            goto error;
663
        }
664
  encrypted_length = krb5_get_wrapped_length(kcontext, heim_crypto,
665
               parms->scopedPduLen);
666
#else                           /* NETSNMP_USE_KERBEROS_MIT */
667
668
        krb5_use_enctype(kcontext, &eblock, subkey->enctype);
669
        retcode = krb5_process_key(kcontext, &eblock, subkey);
670
671
        if (retcode) {
672
            DEBUGMSGTL(("ksm", "krb5_process_key failed: %s\n",
673
                        error_message(retcode)));
674
            snmp_set_detail(error_message(retcode));
675
            retval = SNMPERR_KRB5;
676
            goto error;
677
        }
678
679
        encrypted_length = krb5_encrypt_size(parms->scopedPduLen,
680
                                             eblock.crypto_entry);
681
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
682
683
0
#ifndef OLD_HEIMDAL /* since heimdal allocs the space for us */
684
0
        encrypted_data = malloc(encrypted_length);
685
686
0
        if (!encrypted_data) {
687
0
            DEBUGMSGTL(("ksm",
688
0
                        "KSM: Unable to malloc %d bytes for encrypt "
689
0
                        "buffer: %s\n", (int)parms->scopedPduLen,
690
0
                        strerror(errno)));
691
0
            retval = SNMPERR_MALLOC;
692
#ifndef NETSNMP_USE_KERBEROS_MIT
693
            krb5_finish_key(kcontext, &eblock);
694
#endif                          /* ! NETSNMP_USE_KERBEROS_MIT */
695
696
0
            goto error;
697
0
        }
698
0
#endif /* ! OLD_HEIMDAL */
699
700
        /*
701
         * We need to set up a blank initialization vector for the encryption.
702
         * Use a block of all zero's (which is dependent on the block size
703
         * of the encryption method).
704
         */
705
706
0
#ifdef NETSNMP_USE_KERBEROS_MIT
707
708
0
        retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);
709
710
0
        if (retcode) {
711
0
            DEBUGMSGTL(("ksm",
712
0
                        "Unable to determine crypto block size: %s\n",
713
0
                        error_message(retcode)));
714
0
            snmp_set_detail(error_message(retcode));
715
0
            retval = SNMPERR_KRB5;
716
0
            goto error;
717
0
        }
718
#elif defined (OLD_HEIMDAL) /* NETSNMP_USE_KERBEROS_MIT */
719
#else                           /* NETSNMP_USE_KERBEROS_MIT */
720
721
        blocksize =
722
            krb5_enctype_array[subkey->enctype]->system->block_length;
723
724
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
725
726
0
#ifndef OLD_HEIMDAL /* since allocs the space for us */
727
0
        ivector.data = malloc(blocksize);
728
729
0
        if (!ivector.data) {
730
0
            DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
731
0
                        (int)blocksize));
732
0
            retval = SNMPERR_MALLOC;
733
0
            goto error;
734
0
        }
735
736
0
        ivector.length = blocksize;
737
0
        memset(ivector.data, 0, blocksize);
738
0
#endif /* OLD_HEIMDAL */
739
740
        /*
741
         * Finally!  Do the encryption!
742
         */
743
744
0
#ifdef NETSNMP_USE_KERBEROS_MIT
745
746
0
        input.data = (char *) parms->scopedPdu;
747
0
        input.length = parms->scopedPduLen;
748
0
        output.ciphertext.data = (char *) encrypted_data;
749
0
        output.ciphertext.length = encrypted_length;
750
751
0
        retcode =
752
0
            krb5_c_encrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
753
0
                           &ivector, &input, &output);
754
755
#elif defined OLD_HEIMDAL /* NETSNMP_USE_KERBEROS_MIT */
756
757
  krb5_data_zero(&encrypted_scoped_pdu);
758
  retcode = krb5_encrypt(kcontext, heim_crypto, KSM_KEY_USAGE_ENCRYPTION,
759
             parms->scopedPdu, parms->scopedPduLen,
760
             &encrypted_scoped_pdu);
761
  if (retcode == 0) {
762
    encrypted_length = encrypted_scoped_pdu.length;
763
    encrypted_data = encrypted_scoped_pdu.data;
764
    krb5_data_zero(&encrypted_scoped_pdu);
765
  }
766
#else                           /* NETSNMP_USE_KERBEROS_MIT */
767
768
        retcode = krb5_encrypt(kcontext, (krb5_pointer) parms->scopedPdu,
769
                               (krb5_pointer) encrypted_data,
770
                               parms->scopedPduLen, &eblock, ivector.data);
771
772
        krb5_finish_key(kcontext, &eblock);
773
774
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
775
776
0
        if (retcode) {
777
0
            DEBUGMSGTL(("ksm", "KSM: krb5_encrypt failed: %s\n",
778
0
                        error_message(retcode)));
779
0
            retval = SNMPERR_KRB5;
780
0
            snmp_set_detail(error_message(retcode));
781
0
            goto error;
782
0
        }
783
784
0
  *offset = 0;
785
786
0
        rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
787
0
                                             offset, 1,
788
0
                                             (u_char) (ASN_UNIVERSAL |
789
0
                                                       ASN_PRIMITIVE |
790
0
                                                       ASN_OCTET_STR),
791
0
                                             encrypted_data,
792
0
                                             encrypted_length);
793
794
0
        if (rc == 0) {
795
0
            DEBUGMSGTL(("ksm", "Building encrypted payload failed.\n"));
796
0
            retval = SNMPERR_TOO_LONG;
797
0
            goto error;
798
0
        }
799
800
0
        DEBUGMSGTL(("ksm", "KSM: Encryption complete.\n"));
801
802
0
    } else {
803
        /*
804
         * Plaintext PDU (not encrypted)
805
         */
806
807
0
        if (*parms->wholeMsgLen < parms->scopedPduLen) {
808
0
            DEBUGMSGTL(("ksm", "Not enough room for plaintext PDU.\n"));
809
0
            retval = SNMPERR_TOO_LONG;
810
0
            goto error;
811
0
        }
812
0
    }
813
814
    /*
815
     * Start encoding the msgSecurityParameters
816
     *
817
     * For now, use 0 for the response hint
818
     */
819
820
0
    DEBUGMSGTL(("ksm", "KSM: scopedPdu added to payload\n"));
821
822
0
    seq_offset = *offset;
823
824
0
    rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
825
0
                                      offset, 1,
826
0
                                      (u_char) (ASN_UNIVERSAL |
827
0
                                                ASN_PRIMITIVE |
828
0
                                                ASN_INTEGER),
829
0
                                      (long *) &zero, sizeof(zero));
830
831
0
    if (rc == 0) {
832
0
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
833
0
        retval = SNMPERR_TOO_LONG;
834
0
        goto error;
835
0
    }
836
837
0
    rc = asn_realloc_rbuild_string(wholeMsg, parms->wholeMsgLen,
838
0
                                         offset, 1,
839
0
                                         (u_char) (ASN_UNIVERSAL |
840
0
                                                   ASN_PRIMITIVE |
841
0
                                                   ASN_OCTET_STR),
842
0
                                         (u_char *) outdata.data,
843
0
                                         outdata.length);
844
845
0
    if (rc == 0) {
846
0
        DEBUGMSGTL(("ksm", "Building ksm AP_REQ failed.\n"));
847
0
        retval = SNMPERR_TOO_LONG;
848
0
        goto error;
849
0
    }
850
851
    /*
852
     * If we didn't encrypt the packet, we haven't yet got the subkey.
853
     * Get that now.
854
     */
855
856
0
    if (!subkey) {
857
0
        if (ksm_state)
858
0
            retcode = krb5_auth_con_getrecvsubkey(kcontext, auth_context,
859
0
                                                    &subkey);
860
0
        else
861
0
            retcode = krb5_auth_con_getsendsubkey(kcontext, auth_context,
862
0
                                                   &subkey);
863
0
        if (retcode) {
864
0
            DEBUGMSGTL(("ksm", "krb5_auth_con_getsendsubkey failed: %s\n",
865
0
                        error_message(retcode)));
866
0
            snmp_set_detail(error_message(retcode));
867
0
            retval = SNMPERR_KRB5;
868
0
            goto error;
869
0
        }
870
#ifdef OLD_HEIMDAL
871
   retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
872
        if (retcode) {
873
            DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n",
874
                        error_message(retcode)));
875
            snmp_set_detail(error_message(retcode));
876
            retval = SNMPERR_KRB5;
877
            goto error;
878
        }
879
#endif          /* OLD_HEIMDAL */
880
0
    }
881
882
    /*
883
     * Now, we need to pick the "right" checksum algorithm.  For old
884
     * crypto, just pick CKSUMTYPE_RSA_MD5_DES; for new crypto, pick
885
     * one of the "approved" ones.
886
     */
887
888
0
#ifdef NETSNMP_USE_KERBEROS_MIT
889
0
    retcode = krb5_c_keyed_checksum_types(kcontext, subkey->enctype,
890
0
                                          &numcksumtypes, &cksumtype_array);
891
892
0
    if (retcode) {
893
0
  DEBUGMSGTL(("ksm", "Unable to find appropriate keyed checksum: %s\n",
894
0
        error_message(retcode)));
895
0
  snmp_set_detail(error_message(retcode));
896
0
        retval = SNMPERR_KRB5;
897
0
        goto error;
898
0
    }
899
900
0
    if (numcksumtypes <= 0) {
901
0
  DEBUGMSGTL(("ksm", "We received a list of zero cksumtypes for this "
902
0
        "enctype (%d)\n", subkey->enctype));
903
0
  snmp_set_detail("No valid checksum type for this encryption type");
904
0
  retval = SNMPERR_KRB5;
905
0
  goto error;
906
0
    }
907
908
    /*
909
     * It's not clear to me from the API which checksum you're supposed
910
     * to support, so I'm taking a guess at the first one
911
     */
912
913
0
    cksumtype = cksumtype_array[0];
914
915
0
    krb5_free_cksumtypes(kcontext, cksumtype_array);
916
917
0
    DEBUGMSGTL(("ksm", "KSM: Choosing checksum type of %d (subkey type "
918
0
    "of %d)\n", cksumtype, subkey->enctype));
919
920
0
    retcode = krb5_c_checksum_length(kcontext, cksumtype, &blocksize);
921
922
0
    if (retcode) {
923
0
        DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n",
924
0
                    error_message(retcode)));
925
0
        snmp_set_detail(error_message(retcode));
926
0
        retval = SNMPERR_KRB5;
927
0
        goto error;
928
0
    }
929
930
0
    CHECKSUM_LENGTH(&pdu_checksum) = blocksize;
931
932
#else /* NETSNMP_USE_KERBEROS_MIT */
933
    if (ksm_state)
934
        cksumtype = ksm_state->cksumtype;
935
    else
936
#ifdef OLD_HEIMDAL
937
    {
938
      /* no way to tell what kind of checksum to use without trying */
939
      retval = krb5_create_checksum(kcontext, heim_crypto, 
940
            KSM_KEY_USAGE_CHECKSUM, 0,
941
            parms->scopedPdu, parms->scopedPduLen,
942
            &pdu_checksum);
943
      if (retval) {
944
        DEBUGMSGTL(("ksm", "Unable to create a checksum: %s\n",
945
        error_message(retval)));
946
        snmp_set_detail(error_message(retcode));
947
        retval = SNMPERR_KRB5;
948
        goto error;
949
      }
950
      cksumtype = CHECKSUM_TYPE(&pdu_checksum);
951
    }
952
#else         /* OLD_HEIMDAL */
953
  cksumtype = CKSUMTYPE_RSA_MD5_DES;
954
#endif          /* OLD_HEIMDAL */
955
956
#ifdef OLD_HEIMDAL
957
  if (!krb5_checksum_is_keyed(kcontext, cksumtype)) {
958
#else         /* OLD_HEIMDAL */
959
    if (!is_keyed_cksum(cksumtype)) {
960
#endif        /* OLD_HEIMDAL */
961
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
962
                    cksumtype));
963
        snmp_set_detail("Checksum is not a keyed checksum");
964
        retval = SNMPERR_KRB5;
965
        goto error;
966
    }
967
968
#ifdef OLD_HEIMDAL
969
    if (!krb5_checksum_is_collision_proof(kcontext, cksumtype)) {
970
#else         /* OLD_HEIMDAL */
971
    if (!is_coll_proof_cksum(cksumtype)) {
972
#endif        /* OLD_HEIMDAL */
973
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
974
                    "checksum\n", cksumtype));
975
        snmp_set_detail("Checksum is not a collision-proof checksum");
976
        retval = SNMPERR_KRB5;
977
        goto error;
978
    }
979
980
#ifdef OLD_HEIMDAL
981
    if (CHECKSUM_CONTENTS(&pdu_checksum) != NULL ) {
982
  /* we did the bogus checksum--don't need to ask for the size again
983
   * or initialize cksumtype; just free the bits */
984
  free(CHECKSUM_CONTENTS(&pdu_checksum));
985
  CHECKSUM_CONTENTS(&pdu_checksum) = NULL;
986
    }
987
    else {
988
  retval = krb5_checksumsize(kcontext, cksumtype,
989
           &CHECKSUM_LENGTH(&pdu_checksum));
990
  if (retval) {
991
      DEBUGMSGTL(("ksm", "Unable to determine checksum length: %s\n",
992
      error_message(retval)));
993
      snmp_set_detail(error_message(retcode));
994
      retval = SNMPERR_KRB5;
995
      goto error;
996
  }
997
#else     /* OLD_HEIMDAL */
998
    CHECKSUM_LENGTH(&pdu_checksum) = krb5_checksum_size(kcontext, cksumtype);
999
#endif      /* OLD_HEIMDAL */
1000
    CHECKSUM_TYPE(&pdu_checksum) = cksumtype;
1001
#ifdef OLD_HEIMDAL
1002
    }
1003
#endif      /* OLD_HEIMDAL */
1004
1005
#endif /* NETSNMP_USE_KERBEROS_MIT */
1006
1007
    /*
1008
     * Note that here, we're just leaving blank space for the checksum;
1009
     * we remember where that is, and we'll fill it in later.
1010
     */
1011
1012
0
    while ((*parms->wholeMsgLen - *offset) < CHECKSUM_LENGTH(&pdu_checksum)) {
1013
0
        if (!asn_realloc(wholeMsg, parms->wholeMsgLen)) {
1014
0
            DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
1015
0
            retval = SNMPERR_TOO_LONG;
1016
0
            goto error;
1017
0
        }
1018
0
    }
1019
0
    *offset += CHECKSUM_LENGTH(&pdu_checksum);
1020
0
    cksum_offset = *offset;
1021
0
    memset(*wholeMsg + *parms->wholeMsgLen - cksum_offset, 0,
1022
0
           CHECKSUM_LENGTH(&pdu_checksum));
1023
1024
0
    rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
1025
0
                                         parms->wholeMsgOffset, 1,
1026
0
                                         (u_char) (ASN_UNIVERSAL |
1027
0
                                                   ASN_PRIMITIVE |
1028
0
                                                   ASN_OCTET_STR),
1029
0
                                         CHECKSUM_LENGTH(&pdu_checksum));
1030
1031
0
    if (rc == 0) {
1032
0
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
1033
0
        retval = SNMPERR_TOO_LONG;
1034
0
        goto error;
1035
0
    }
1036
1037
0
    tmp = cksumtype;
1038
0
    rc = asn_realloc_rbuild_int(wholeMsg, parms->wholeMsgLen,
1039
0
                                      parms->wholeMsgOffset, 1,
1040
0
                                      (u_char) (ASN_UNIVERSAL |
1041
0
                                                ASN_PRIMITIVE |
1042
0
                                                ASN_INTEGER),
1043
0
                                      &tmp, sizeof(tmp));
1044
1045
0
    if (rc == 0) {
1046
0
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
1047
0
        retval = SNMPERR_TOO_LONG;
1048
0
        goto error;
1049
0
    }
1050
1051
0
    rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
1052
0
                                           parms->wholeMsgOffset, 1,
1053
0
                                           (u_char) (ASN_SEQUENCE |
1054
0
                                                     ASN_CONSTRUCTOR),
1055
0
                                           *offset - seq_offset);
1056
1057
0
    if (rc == 0) {
1058
0
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
1059
0
        retval = SNMPERR_TOO_LONG;
1060
0
        goto error;
1061
0
    }
1062
1063
0
    rc = asn_realloc_rbuild_header(wholeMsg, parms->wholeMsgLen,
1064
0
                                         parms->wholeMsgOffset, 1,
1065
0
                                         (u_char) (ASN_UNIVERSAL |
1066
0
                                                   ASN_PRIMITIVE |
1067
0
                                                   ASN_OCTET_STR),
1068
0
                                         *offset - seq_offset);
1069
1070
0
    if (rc == 0) {
1071
0
        DEBUGMSGTL(("ksm", "Building ksm security parameters failed.\n"));
1072
0
        retval = SNMPERR_TOO_LONG;
1073
0
        goto error;
1074
0
    }
1075
1076
0
    DEBUGMSGTL(("ksm", "KSM: Security parameter encoding completed\n"));
1077
1078
    /*
1079
     * We're done with the KSM security parameters - now we do the global
1080
     * header and wrap up the whole PDU.
1081
     */
1082
1083
0
    while ((*parms->wholeMsgLen - *offset) < parms->globalDataLen) {
1084
0
        if (!asn_realloc(wholeMsg, parms->wholeMsgLen)) {
1085
0
            DEBUGMSGTL(("ksm", "Building global data failed.\n"));
1086
0
            retval = SNMPERR_TOO_LONG;
1087
0
            goto error;
1088
0
        }
1089
0
    }
1090
1091
0
    *offset += parms->globalDataLen;
1092
0
    memcpy(*wholeMsg + *parms->wholeMsgLen - *offset,
1093
0
     parms->globalData, parms->globalDataLen);
1094
1095
0
    rc = asn_realloc_rbuild_sequence(wholeMsg, parms->wholeMsgLen,
1096
0
                                           offset, 1,
1097
0
                                           (u_char) (ASN_SEQUENCE |
1098
0
                                                     ASN_CONSTRUCTOR),
1099
0
                                           *offset);
1100
1101
0
    if (rc == 0) {
1102
0
        DEBUGMSGTL(("ksm", "Building master packet sequence.\n"));
1103
0
        retval = SNMPERR_TOO_LONG;
1104
0
        goto error;
1105
0
    }
1106
1107
0
    DEBUGMSGTL(("ksm", "KSM: PDU master packet encoding complete.\n"));
1108
1109
    /*
1110
     * Now we need to checksum the entire PDU (since it's built).
1111
     */
1112
1113
0
#ifndef OLD_HEIMDAL /* since heimdal allocs the mem for us */
1114
0
    CHECKSUM_CONTENTS(&pdu_checksum) = malloc(CHECKSUM_LENGTH(&pdu_checksum));
1115
1116
0
    if (!CHECKSUM_CONTENTS(&pdu_checksum)) {
1117
0
        DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum\n",
1118
0
                    CHECKSUM_LENGTH(&pdu_checksum)));
1119
0
        retval = SNMPERR_MALLOC;
1120
0
        goto error;
1121
0
    }
1122
0
#endif          /* ! OLD_HEIMDAL */
1123
0
#ifdef NETSNMP_USE_KERBEROS_MIT
1124
1125
0
    input.data = (char *) (*wholeMsg + *parms->wholeMsgLen - *offset);
1126
0
    input.length = *offset;
1127
0
        retcode = krb5_c_make_checksum(kcontext, cksumtype, subkey,
1128
0
                                       KSM_KEY_USAGE_CHECKSUM, &input,
1129
0
                                       &pdu_checksum);
1130
1131
#elif defined(OLD_HEIMDAL)  /* NETSNMP_USE_KERBEROS_MIT */
1132
1133
  retcode = krb5_create_checksum(kcontext, heim_crypto,
1134
               KSM_KEY_USAGE_CHECKSUM, cksumtype,
1135
               *wholeMsg + *parms->wholeMsgLen
1136
               - *offset, *offset, &pdu_checksum);
1137
#else                           /* NETSNMP_USE_KERBEROS_MIT */
1138
1139
    retcode = krb5_calculate_checksum(kcontext, cksumtype, *wholeMsg +
1140
              *parms->wholeMsgLen - *offset,
1141
                                      *offset,
1142
                                      (krb5_pointer) subkey->contents,
1143
                                      subkey->length, &pdu_checksum);
1144
1145
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
1146
1147
0
    if (retcode) {
1148
0
        DEBUGMSGTL(("ksm", "Calculate checksum failed: %s\n",
1149
0
                    error_message(retcode)));
1150
0
        retval = SNMPERR_KRB5;
1151
0
        snmp_set_detail(error_message(retcode));
1152
0
        goto error;
1153
0
    }
1154
1155
0
    DEBUGMSGTL(("ksm", "KSM: Checksum calculation complete.\n"));
1156
1157
0
    memcpy(*wholeMsg + *parms->wholeMsgLen - cksum_offset,
1158
0
           CHECKSUM_CONTENTS(&pdu_checksum), CHECKSUM_LENGTH(&pdu_checksum));
1159
1160
0
    DEBUGMSGTL(("ksm", "KSM: Writing checksum of %d bytes at offset %d\n",
1161
0
                (int)CHECKSUM_LENGTH(&pdu_checksum),
1162
0
    (int)(*parms->wholeMsgLen - cksum_offset - 1)));
1163
1164
0
    DEBUGMSGTL(("ksm", "KSM: Checksum:"));
1165
1166
0
    for (i = 0; i < CHECKSUM_LENGTH(&pdu_checksum); i++)
1167
0
        DEBUGMSG(("ksm", " %02x",
1168
0
                  (unsigned int) ((unsigned char *)CHECKSUM_CONTENTS(&pdu_checksum))[i]));
1169
1170
0
    DEBUGMSG(("ksm", "\n"));
1171
1172
    /*
1173
     * If we're _not_ called as part of a response (null ksm_state),
1174
     * then save the auth_context for later using our cache routines.
1175
     */
1176
1177
0
    if (!ksm_state) {
1178
0
        if ((retval = ksm_insert_cache(parms->pdu->msgid, auth_context,
1179
0
                                       (u_char *) parms->secName,
1180
0
                                       parms->secNameLen)) !=
1181
0
            SNMPERR_SUCCESS)
1182
0
            goto error;
1183
0
        auth_context = NULL;
1184
0
    }
1185
1186
0
    DEBUGMSGTL(("ksm", "KSM processing complete!\n"));
1187
1188
0
  error:
1189
1190
0
    if (CHECKSUM_CONTENTS(&pdu_checksum))
1191
0
#ifdef NETSNMP_USE_KERBEROS_MIT
1192
0
        krb5_free_checksum_contents(kcontext, &pdu_checksum);
1193
#else                           /* NETSNMP_USE_KERBEROS_MIT */
1194
        free(CHECKSUM_CONTENTS(&pdu_checksum));
1195
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
1196
1197
0
    if (ivector.data)
1198
0
        free(ivector.data);
1199
1200
0
    if (subkey)
1201
0
        krb5_free_keyblock(kcontext, subkey);
1202
1203
#ifdef OLD_HEIMDAL /* OLD_HEIMDAL */
1204
    if (heim_crypto)
1205
      krb5_crypto_destroy(kcontext, heim_crypto);
1206
#endif /* OLD_HEIMDAL */
1207
1208
0
    if (encrypted_data)
1209
0
        free(encrypted_data);
1210
1211
0
    if (outdata.data)
1212
0
        free(outdata.data);
1213
1214
0
    if (cc)
1215
0
        krb5_cc_close(kcontext, cc);
1216
1217
0
    if (auth_context && !ksm_state)
1218
0
        krb5_auth_con_free(kcontext, auth_context);
1219
1220
0
    return retval;
1221
0
}
1222
1223
/****************************************************************************
1224
 *
1225
 * ksm_process_in_msg
1226
 *
1227
 * Parameters:
1228
 *  (See list below...)
1229
 *
1230
 * Returns:
1231
 *  KSM_ERR_NO_ERROR                        On success.
1232
 *  SNMPERR_KRB5
1233
 *  KSM_ERR_GENERIC_ERROR
1234
 *  KSM_ERR_UNSUPPORTED_SECURITY_LEVEL
1235
 *
1236
 *
1237
 * Processes an incoming message.
1238
 *
1239
 ****************************************************************************/
1240
1241
int
1242
ksm_process_in_msg(struct snmp_secmod_incoming_params *parms)
1243
0
{
1244
0
    long            temp;
1245
0
    krb5_cksumtype  cksumtype;
1246
0
    krb5_auth_context auth_context = NULL;
1247
0
    krb5_error_code retcode;
1248
0
    krb5_checksum   checksum;
1249
0
    krb5_data       ap_req, ivector;
1250
0
    krb5_flags      flags;
1251
0
    krb5_keyblock  *subkey = NULL;
1252
0
#ifdef NETSNMP_USE_KERBEROS_MIT
1253
0
    krb5_data       input, output;
1254
0
    krb5_boolean    valid;
1255
0
    krb5_enc_data   in_crypt;
1256
#elif defined OLD_HEIMDAL /* NETSNMP_USE_KERBEROS_MIT */
1257
    krb5_data output;
1258
    krb5_crypto heim_crypto = NULL;
1259
#else                           /* NETSNMP_USE_KERBEROS_MIT */
1260
    krb5_encrypt_block eblock;
1261
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
1262
0
    krb5_ticket    *ticket = NULL;
1263
0
    int             retval = SNMPERR_SUCCESS, response = 0;
1264
0
    size_t          length =
1265
0
        parms->wholeMsgLen - (u_int) (parms->secParams - parms->wholeMsg);
1266
0
    u_char         *current = parms->secParams, type;
1267
0
#ifndef OLD_HEIMDAL
1268
0
    size_t          blocksize;
1269
0
#endif
1270
0
    size_t          cksumlength;
1271
0
    long            hint;
1272
0
    char           *cname;
1273
0
    struct ksm_secStateRef *ksm_state;
1274
0
    struct ksm_cache_entry *entry;
1275
1276
0
    DEBUGMSGTL(("ksm", "Processing has begun\n"));
1277
1278
0
    CHECKSUM_CONTENTS(&checksum) = NULL;
1279
0
    ap_req.data = NULL;
1280
0
    ivector.length = 0;
1281
0
    ivector.data = NULL;
1282
1283
    /*
1284
     * First, parse the security parameters (because we need the subkey inside
1285
     * of the ticket to do anything
1286
     */
1287
1288
0
    if ((current = asn_parse_sequence(current, &length, &type,
1289
0
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
1290
0
                                       ASN_OCTET_STR),
1291
0
                                      "ksm first octet")) == NULL) {
1292
0
        DEBUGMSGTL(("ksm", "Initial security paramter parsing failed\n"));
1293
1294
0
        retval = SNMPERR_ASN_PARSE_ERR;
1295
0
        goto error;
1296
0
    }
1297
1298
0
    if ((current = asn_parse_sequence(current, &length, &type,
1299
0
                                      (ASN_SEQUENCE | ASN_CONSTRUCTOR),
1300
0
                                      "ksm sequence")) == NULL) {
1301
0
        DEBUGMSGTL(("ksm",
1302
0
                    "Security parameter sequence parsing failed\n"));
1303
1304
0
        retval = SNMPERR_ASN_PARSE_ERR;
1305
0
        goto error;
1306
0
    }
1307
1308
0
    if ((current = asn_parse_int(current, &length, &type, &temp,
1309
0
                                 sizeof(temp))) == NULL) {
1310
0
        DEBUGMSGTL(("ksm", "Security parameter checksum type parsing"
1311
0
                    "failed\n"));
1312
1313
0
        retval = SNMPERR_ASN_PARSE_ERR;
1314
0
        goto error;
1315
0
    }
1316
1317
0
    cksumtype = temp;
1318
1319
0
#ifdef NETSNMP_USE_KERBEROS_MIT
1320
0
    if (!krb5_c_valid_cksumtype(cksumtype)) {
1321
0
        DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));
1322
1323
0
        retval = SNMPERR_KRB5;
1324
0
        snmp_set_detail("Invalid checksum type");
1325
0
        goto error;
1326
0
    }
1327
1328
0
    if (!krb5_c_is_keyed_cksum(cksumtype)) {
1329
0
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
1330
0
                    cksumtype));
1331
0
        snmp_set_detail("Checksum is not a keyed checksum");
1332
0
        retval = SNMPERR_KRB5;
1333
0
        goto error;
1334
0
    }
1335
1336
0
    if (!krb5_c_is_coll_proof_cksum(cksumtype)) {
1337
0
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
1338
0
                    "checksum\n", cksumtype));
1339
0
        snmp_set_detail("Checksum is not a collision-proof checksum");
1340
0
        retval = SNMPERR_KRB5;
1341
0
        goto error;
1342
0
    }
1343
#else /* ! NETSNMP_USE_KERBEROS_MIT */
1344
#ifdef OLD_HEIMDAL
1345
    /* kludge */
1346
    if (krb5_checksumsize(kcontext, cksumtype, &cksumlength)) {
1347
#else         /* OLD_HEIMDAL */
1348
    if (!valid_cksumtype(cksumtype)) {
1349
#endif          /* OLD_HEIMDAL */
1350
        DEBUGMSGTL(("ksm", "Invalid checksum type (%d)\n", cksumtype));
1351
1352
        retval = SNMPERR_KRB5;
1353
        snmp_set_detail("Invalid checksum type");
1354
        goto error;
1355
    }
1356
1357
#ifdef OLD_HEIMDAL
1358
    if (!krb5_checksum_is_keyed(kcontext, cksumtype)) {
1359
#else         /* OLD_HEIMDAL */
1360
    if (!is_keyed_cksum(cksumtype)) {
1361
#endif          /* OLD_HEIMDAL */
1362
        DEBUGMSGTL(("ksm", "Checksum type %d is not a keyed checksum\n",
1363
                    cksumtype));
1364
        snmp_set_detail("Checksum is not a keyed checksum");
1365
        retval = SNMPERR_KRB5;
1366
        goto error;
1367
    }
1368
1369
#ifdef OLD_HEIMDAL
1370
    if (!krb5_checksum_is_collision_proof(kcontext, cksumtype)) {
1371
#else         /* OLD_HEIMDAL */
1372
    if (!is_coll_proof_cksum(cksumtype)) {
1373
#endif          /* OLD_HEIMDAL */
1374
        DEBUGMSGTL(("ksm", "Checksum type %d is not a collision-proof "
1375
                    "checksum\n", cksumtype));
1376
        snmp_set_detail("Checksum is not a collision-proof checksum");
1377
        retval = SNMPERR_KRB5;
1378
        goto error;
1379
    }
1380
#endif /* NETSNMP_USE_KERBEROS_MIT */
1381
1382
0
    CHECKSUM_TYPE(&checksum) = cksumtype;
1383
1384
0
    cksumlength = length;
1385
1386
0
    if ((current = asn_parse_sequence(current, &cksumlength, &type,
1387
0
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
1388
0
                                       ASN_OCTET_STR), "ksm checksum")) ==
1389
0
        NULL) {
1390
0
        DEBUGMSGTL(("ksm",
1391
0
                    "Security parameter checksum parsing failed\n"));
1392
1393
0
        retval = SNMPERR_ASN_PARSE_ERR;
1394
0
        goto error;
1395
0
    }
1396
1397
0
    CHECKSUM_CONTENTS(&checksum) = malloc(cksumlength);
1398
0
    if (!CHECKSUM_CONTENTS(&checksum)) {
1399
0
        DEBUGMSGTL(("ksm", "Unable to malloc %d bytes for checksum.\n",
1400
0
                    (int)cksumlength));
1401
0
        retval = SNMPERR_MALLOC;
1402
0
        goto error;
1403
0
    }
1404
1405
0
    memcpy(CHECKSUM_CONTENTS(&checksum), current, cksumlength);
1406
1407
0
    CHECKSUM_LENGTH(&checksum) = cksumlength;
1408
0
    CHECKSUM_TYPE(&checksum) = cksumtype;
1409
1410
    /*
1411
     * Zero out the checksum so the validation works correctly
1412
     */
1413
1414
0
    memset(current, 0, cksumlength);
1415
1416
0
    current += cksumlength;
1417
0
    length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);
1418
1419
0
    if ((current = asn_parse_sequence(current, &length, &type,
1420
0
                                      (ASN_UNIVERSAL | ASN_PRIMITIVE |
1421
0
                                       ASN_OCTET_STR), "ksm ap_req")) ==
1422
0
        NULL) {
1423
0
        DEBUGMSGTL(("ksm", "KSM security parameter AP_REQ/REP parsing "
1424
0
                    "failed\n"));
1425
1426
0
        retval = SNMPERR_ASN_PARSE_ERR;
1427
0
        goto error;
1428
0
    }
1429
1430
0
    ap_req.length = length;
1431
0
    ap_req.data = malloc(length);
1432
0
    if (!ap_req.data) {
1433
0
        DEBUGMSGTL(("ksm",
1434
0
                    "KSM unable to malloc %d bytes for AP_REQ/REP.\n",
1435
0
                    (int)length));
1436
0
        retval = SNMPERR_MALLOC;
1437
0
        goto error;
1438
0
    }
1439
1440
0
    memcpy(ap_req.data, current, length);
1441
1442
0
    current += length;
1443
0
    length = parms->wholeMsgLen - (u_int) (current - parms->wholeMsg);
1444
1445
0
    if ((current = asn_parse_int(current, &length, &type, &hint,
1446
0
                                 sizeof(hint))) == NULL) {
1447
0
        DEBUGMSGTL(("ksm",
1448
0
                    "KSM security parameter hint parsing failed\n"));
1449
1450
0
        retval = SNMPERR_ASN_PARSE_ERR;
1451
0
        goto error;
1452
0
    }
1453
1454
    /*
1455
     * Okay!  We've got it all!  Now try decoding the damn ticket.
1456
     *
1457
     * But of course there's a WRINKLE!  We need to figure out if we're
1458
     * processing a AP_REQ or an AP_REP.  How do we do that?  We're going
1459
     * to cheat, and look at the first couple of bytes (which is what
1460
     * the Kerberos library routines do anyway).
1461
     *
1462
     * If there are ever new Kerberos message formats, we'll need to fix
1463
     * this here.
1464
     *
1465
     * If it's a _response_, then we need to get the auth_context
1466
     * from our cache.
1467
     */
1468
1469
0
    if (ap_req.length
1470
0
#ifndef NETSNMP_USE_KERBEROS_HEIMDAL
1471
0
        && (ap_req.data[0] == 0x6e || ap_req.data[0] == 0x4e)) {
1472
#else       /* NETSNMP_USE_KERBEROS_HEIMDAL */
1473
        && (((char *)ap_req.data)[0] == 0x6e || ((char *)ap_req.data)[0] == 0x4e)) {
1474
#endif
1475
1476
        /*
1477
         * We need to initialize the authorization context, and set the
1478
         * replay cache in it (and initialize the replay cache if we
1479
         * haven't already
1480
         */
1481
1482
0
        retcode = krb5_auth_con_init(kcontext, &auth_context);
1483
1484
0
        if (retcode) {
1485
0
            DEBUGMSGTL(("ksm", "krb5_auth_con_init failed: %s\n",
1486
0
                        error_message(retcode)));
1487
0
            retval = SNMPERR_KRB5;
1488
0
            snmp_set_detail(error_message(retcode));
1489
0
            goto error;
1490
0
        }
1491
1492
0
        if (!rcache) {
1493
0
            krb5_data       server;
1494
0
            server.data = service_host;
1495
0
            server.length = strlen(server.data);
1496
1497
0
            retcode = krb5_get_server_rcache(kcontext, &server, &rcache);
1498
1499
0
            if (retcode) {
1500
0
                DEBUGMSGTL(("ksm", "krb5_get_server_rcache failed: %s\n",
1501
0
                            error_message(retcode)));
1502
0
                retval = SNMPERR_KRB5;
1503
0
                snmp_set_detail(error_message(retcode));
1504
0
                goto error;
1505
0
            }
1506
0
        }
1507
1508
0
        retcode = krb5_auth_con_setrcache(kcontext, auth_context, rcache);
1509
1510
0
        if (retcode) {
1511
0
            DEBUGMSGTL(("ksm", "krb5_auth_con_setrcache failed: %s\n",
1512
0
                        error_message(retcode)));
1513
0
            retval = SNMPERR_KRB5;
1514
0
            snmp_set_detail(error_message(retcode));
1515
0
            goto error;
1516
0
        }
1517
1518
0
        retcode = krb5_rd_req(kcontext, &auth_context, &ap_req, NULL,
1519
0
                              keytab, &flags, &ticket);
1520
1521
0
        krb5_auth_con_setrcache(kcontext, auth_context, NULL);
1522
1523
0
        if (retcode) {
1524
0
            DEBUGMSGTL(("ksm", "krb5_rd_req() failed: %s\n",
1525
0
                        error_message(retcode)));
1526
0
            retval = SNMPERR_KRB5;
1527
0
            snmp_set_detail(error_message(retcode));
1528
0
            goto error;
1529
0
        }
1530
1531
0
        retcode =
1532
0
            krb5_unparse_name(kcontext, TICKET_CLIENT(ticket), &cname);
1533
1534
0
        if (retcode == 0) {
1535
0
            DEBUGMSGTL(("ksm", "KSM authenticated principal name: %s\n",
1536
0
                        cname));
1537
0
            free(cname);
1538
0
        }
1539
1540
        /*
1541
         * Check to make sure AP_OPTS_MUTUAL_REQUIRED was set
1542
         */
1543
1544
0
        if (!(flags & AP_OPTS_MUTUAL_REQUIRED)) {
1545
0
            DEBUGMSGTL(("ksm",
1546
0
                        "KSM MUTUAL_REQUIRED not set in request!\n"));
1547
0
            retval = SNMPERR_KRB5;
1548
0
            snmp_set_detail("MUTUAL_REQUIRED not set in message");
1549
0
            goto error;
1550
0
        }
1551
1552
0
        retcode =
1553
0
            krb5_auth_con_getrecvsubkey(kcontext, auth_context, &subkey);
1554
1555
0
        if (retcode) {
1556
0
            DEBUGMSGTL(("ksm", "KSM remote subkey retrieval failed: %s\n",
1557
0
                        error_message(retcode)));
1558
0
            retval = SNMPERR_KRB5;
1559
0
            snmp_set_detail(error_message(retcode));
1560
0
            goto error;
1561
0
        }
1562
1563
0
#ifndef NETSNMP_USE_KERBEROS_HEIMDAL
1564
0
    } else if (ap_req.length && (ap_req.data[0] == 0x6f ||
1565
0
                                 ap_req.data[0] == 0x4f)) {
1566
#else       /* NETSNMP_USE_KERBEROS_HEIMDAL */
1567
    } else if (ap_req.length && (((char *)ap_req.data)[0] == 0x6f ||
1568
                                 ((char *)ap_req.data)[0] == 0x4f)) {
1569
#endif        /* NETSNMP_USE_KERBEROS_HEIMDAL */
1570
        /*
1571
         * Looks like a response; let's see if we've got that auth_context
1572
         * in our cache.
1573
         */
1574
1575
0
        krb5_ap_rep_enc_part *repl = NULL;
1576
1577
0
        response = 1;
1578
1579
0
        entry = ksm_get_cache(parms->pdu->msgid);
1580
1581
0
        if (!entry) {
1582
0
            DEBUGMSGTL(("ksm",
1583
0
                        "KSM: Unable to find auth_context for PDU with "
1584
0
                        "message ID of %ld\n", parms->pdu->msgid));
1585
0
            retval = SNMPERR_KRB5;
1586
0
            goto error;
1587
0
        }
1588
1589
0
        auth_context = entry->auth_context;
1590
1591
        /*
1592
         * In that case, let's call the rd_rep function
1593
         */
1594
1595
0
        retcode = krb5_rd_rep(kcontext, auth_context, &ap_req, &repl);
1596
1597
0
        if (repl)
1598
0
            krb5_free_ap_rep_enc_part(kcontext, repl);
1599
1600
0
        if (retcode) {
1601
0
            DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() failed: %s\n",
1602
0
                        error_message(retcode)));
1603
0
            retval = SNMPERR_KRB5;
1604
0
            goto error;
1605
0
        }
1606
1607
0
        DEBUGMSGTL(("ksm", "KSM: krb5_rd_rep() decoded successfully.\n"));
1608
1609
0
        retcode =
1610
0
            krb5_auth_con_getsendsubkey(kcontext, auth_context, &subkey);
1611
1612
0
        if (retcode) {
1613
0
            DEBUGMSGTL(("ksm", "Unable to retrieve local subkey: %s\n",
1614
0
                        error_message(retcode)));
1615
0
            retval = SNMPERR_KRB5;
1616
0
            snmp_set_detail("Unable to retrieve local subkey");
1617
0
            goto error;
1618
0
        }
1619
1620
0
    } else {
1621
0
#ifndef NETSNMP_USE_KERBEROS_HEIMDAL
1622
0
        DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n",
1623
0
                    ap_req.length ? (unsigned char)ap_req.data[0] : 0));
1624
#else         /* NETSNMP_USE_KERBEROS_HEIMDAL */
1625
   DEBUGMSGTL(("ksm", "Unknown Kerberos message type (%02x)\n",
1626
                    ap_req.length ? ((unsigned char *)ap_req.data)[0] : 0));
1627
#endif
1628
0
        retval = SNMPERR_KRB5;
1629
0
        snmp_set_detail("Unknown Kerberos message type");
1630
0
        goto error;
1631
0
    }
1632
1633
0
#ifdef NETSNMP_USE_KERBEROS_MIT
1634
0
    input.data = (char *) parms->wholeMsg;
1635
0
    input.length = parms->wholeMsgLen;
1636
1637
0
    retcode =
1638
0
        krb5_c_verify_checksum(kcontext, subkey, KSM_KEY_USAGE_CHECKSUM,
1639
0
                               &input, &checksum, &valid);
1640
#elif defined(OLD_HEIMDAL)  /* NETSNMP_USE_KERBEROS_MIT */
1641
    retcode = krb5_crypto_init(kcontext, subkey, 0, &heim_crypto);
1642
    if (retcode) {
1643
            DEBUGMSGTL(("ksm", "krb5_crypto_init failed: %s\n",
1644
                        error_message(retcode)));
1645
            snmp_set_detail(error_message(retcode));
1646
            retval = SNMPERR_KRB5;
1647
            goto error;
1648
    }
1649
    retcode = krb5_verify_checksum(kcontext, heim_crypto,
1650
           KSM_KEY_USAGE_CHECKSUM, parms->wholeMsg,
1651
           parms->wholeMsgLen, &checksum);
1652
#else                           /* NETSNMP_USE_KERBEROS_MIT */
1653
    retcode = krb5_verify_checksum(kcontext, cksumtype, &checksum,
1654
                                   parms->wholeMsg, parms->wholeMsgLen,
1655
                                   (krb5_pointer) subkey->contents,
1656
                                   subkey->length);
1657
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
1658
1659
0
    if (retcode) {
1660
0
        DEBUGMSGTL(("ksm", "KSM checksum verification failed: %s\n",
1661
0
                    error_message(retcode)));
1662
0
        retval = SNMPERR_KRB5;
1663
0
        snmp_set_detail(error_message(retcode));
1664
0
        goto error;
1665
0
    }
1666
1667
    /*
1668
     * Don't ask me why they didn't simply return an error, but we have
1669
     * to check to see if "valid" is false.
1670
     */
1671
1672
0
#ifdef NETSNMP_USE_KERBEROS_MIT
1673
0
    if (!valid) {
1674
0
        DEBUGMSGTL(("ksm", "Computed checksum did not match supplied "
1675
0
                    "checksum!\n"));
1676
0
        retval = SNMPERR_KRB5;
1677
0
        snmp_set_detail
1678
0
            ("Computed checksum did not match supplied checksum");
1679
0
        goto error;
1680
0
    }
1681
0
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
1682
1683
    /*
1684
     * Handle an encrypted PDU.  Note that it's an OCTET_STRING of the
1685
     * output of whatever Kerberos cryptosystem you're using (defined by
1686
     * the encryption type).  Note that this is NOT the EncryptedData
1687
     * sequence - it's what goes in the "cipher" field of EncryptedData.
1688
     */
1689
1690
0
    if (parms->secLevel == SNMP_SEC_LEVEL_AUTHPRIV) {
1691
1692
0
        if ((current = asn_parse_sequence(current, &length, &type,
1693
0
                                          (ASN_UNIVERSAL | ASN_PRIMITIVE |
1694
0
                                           ASN_OCTET_STR), "ksm pdu")) ==
1695
0
            NULL) {
1696
0
            DEBUGMSGTL(("ksm", "KSM sPDU octet decoding failed\n"));
1697
0
            retval = SNMPERR_ASN_PARSE_ERR;
1698
0
            goto error;
1699
0
        }
1700
1701
        /*
1702
         * The PDU is now pointed at by "current", and the length is in
1703
         * "length".
1704
         */
1705
1706
0
        DEBUGMSGTL(("ksm", "KSM starting sPDU decode\n"));
1707
1708
        /*
1709
         * We need to set up a blank initialization vector for the decryption.
1710
         * Use a block of all zero's (which is dependent on the block size
1711
         * of the encryption method).
1712
         */
1713
1714
0
#ifdef NETSNMP_USE_KERBEROS_MIT
1715
1716
0
        retcode = krb5_c_block_size(kcontext, subkey->enctype, &blocksize);
1717
1718
0
        if (retcode) {
1719
0
            DEBUGMSGTL(("ksm",
1720
0
                        "Unable to determine crypto block size: %s\n",
1721
0
                        error_message(retcode)));
1722
0
            snmp_set_detail(error_message(retcode));
1723
0
            retval = SNMPERR_KRB5;
1724
0
            goto error;
1725
0
        }
1726
#elif defined(OLD_HEIMDAL)  /* NETSNMP_USE_KERBEROS_MIT */
1727
#else                           /* NETSNMP_USE_KERBEROS_MIT */
1728
1729
        blocksize =
1730
            krb5_enctype_array[subkey->enctype]->system->block_length;
1731
1732
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
1733
1734
0
#ifndef OLD_HEIMDAL
1735
0
        ivector.data = malloc(blocksize);
1736
1737
0
        if (!ivector.data) {
1738
0
            DEBUGMSGTL(("ksm", "Unable to allocate %d bytes for ivector\n",
1739
0
                        (int)blocksize));
1740
0
            retval = SNMPERR_MALLOC;
1741
0
            goto error;
1742
0
        }
1743
1744
0
        ivector.length = blocksize;
1745
0
        memset(ivector.data, 0, blocksize);
1746
1747
#ifndef NETSNMP_USE_KERBEROS_MIT
1748
1749
        krb5_use_enctype(kcontext, &eblock, subkey->enctype);
1750
1751
        retcode = krb5_process_key(kcontext, &eblock, subkey);
1752
1753
        if (retcode) {
1754
            DEBUGMSGTL(("ksm", "KSM key post-processing failed: %s\n",
1755
                        error_message(retcode)));
1756
            snmp_set_detail(error_message(retcode));
1757
            retval = SNMPERR_KRB5;
1758
            goto error;
1759
        }
1760
#endif                          /* !NETSNMP_USE_KERBEROS_MIT */
1761
1762
0
#endif /* ! OLD_HEIMDAL */
1763
1764
0
        if (length > *parms->scopedPduLen) {
1765
0
            DEBUGMSGTL(("ksm", "KSM not enough room - have %d bytes to "
1766
0
                        "decrypt but only %d bytes available\n", (int)length,
1767
0
                        (int)*parms->scopedPduLen));
1768
0
            retval = SNMPERR_TOO_LONG;
1769
#ifndef NETSNMP_USE_KERBEROS_MIT
1770
#ifndef OLD_HEIMDAL
1771
            krb5_finish_key(kcontext, &eblock);
1772
#endif                          /* ! OLD_HEIMDAL */
1773
#endif                          /* ! NETSNMP_USE_KERBEROS_MIT */
1774
0
            goto error;
1775
0
        }
1776
0
#ifdef NETSNMP_USE_KERBEROS_MIT
1777
0
        in_crypt.ciphertext.data = (char *) current;
1778
0
        in_crypt.ciphertext.length = length;
1779
0
        in_crypt.enctype = subkey->enctype;
1780
0
        output.data = (char *) *parms->scopedPdu;
1781
0
        output.length = *parms->scopedPduLen;
1782
1783
0
        retcode =
1784
0
            krb5_c_decrypt(kcontext, subkey, KSM_KEY_USAGE_ENCRYPTION,
1785
0
                           &ivector, &in_crypt, &output);
1786
#elif defined (OLD_HEIMDAL) /* NETSNMP_USE_KERBEROS_MIT */
1787
  retcode = krb5_decrypt(kcontext, heim_crypto, KSM_KEY_USAGE_ENCRYPTION,
1788
             current, length, &output);
1789
  if (retcode == 0) {
1790
    *parms->scopedPdu = (u_char *) output.data;
1791
    *parms->scopedPduLen = output.length;
1792
    krb5_data_zero(&output);
1793
  }
1794
#else                           /* NETSNMP_USE_KERBEROS_MIT */
1795
1796
        retcode = krb5_decrypt(kcontext, (krb5_pointer) current,
1797
                               *parms->scopedPdu, length, &eblock,
1798
                               ivector.data);
1799
1800
        krb5_finish_key(kcontext, &eblock);
1801
1802
#endif                          /* NETSNMP_USE_KERBEROS_MIT */
1803
1804
0
        if (retcode) {
1805
0
            DEBUGMSGTL(("ksm", "Decryption failed: %s\n",
1806
0
                        error_message(retcode)));
1807
0
            snmp_set_detail(error_message(retcode));
1808
0
            retval = SNMPERR_KRB5;
1809
0
            goto error;
1810
0
        }
1811
1812
0
        *parms->scopedPduLen = length;
1813
1814
0
    } else {
1815
        /*
1816
         * Clear PDU
1817
         */
1818
1819
0
        *parms->scopedPdu = current;
1820
0
        *parms->scopedPduLen =
1821
0
            parms->wholeMsgLen - (current - parms->wholeMsg);
1822
0
    }
1823
1824
    /*
1825
     * A HUGE GROSS HACK
1826
     */
1827
1828
0
    *parms->maxSizeResponse = parms->maxMsgSize - 200;
1829
1830
0
    DEBUGMSGTL(("ksm", "KSM processing complete\n"));
1831
1832
    /*
1833
     * Set the secName to the right value (a hack for now).  But that's
1834
     * only used for when we're processing a request, not a response.
1835
     */
1836
1837
0
    if (!response) {
1838
1839
0
        retcode = krb5_unparse_name(kcontext, TICKET_CLIENT(ticket),
1840
0
                                    &cname);
1841
1842
0
        if (retcode) {
1843
0
            DEBUGMSGTL(("ksm", "KSM krb5_unparse_name failed: %s\n",
1844
0
                        error_message(retcode)));
1845
0
            snmp_set_detail(error_message(retcode));
1846
0
            retval = SNMPERR_KRB5;
1847
0
            goto error;
1848
0
        }
1849
1850
0
        if (strlen(cname) >= *parms->secNameLen) {
1851
0
            DEBUGMSGTL(("ksm",
1852
0
                        "KSM: Principal length (%d) is too long (%d)\n",
1853
0
                        (int)strlen(cname), (int)*parms->secNameLen));
1854
0
            retval = SNMPERR_TOO_LONG;
1855
0
            free(cname);
1856
0
            goto error;
1857
0
        }
1858
1859
0
        strcpy(parms->secName, cname);
1860
0
        *parms->secNameLen = strlen(cname);
1861
1862
0
        free(cname);
1863
1864
        /*
1865
         * Also, if we're not a response, keep around our auth_context so we
1866
         * can encode the reply message correctly
1867
         */
1868
1869
0
        ksm_state = SNMP_MALLOC_STRUCT(ksm_secStateRef);
1870
1871
0
        if (!ksm_state) {
1872
0
            DEBUGMSGTL(("ksm", "KSM unable to malloc memory for "
1873
0
                        "ksm_secStateRef\n"));
1874
0
            retval = SNMPERR_MALLOC;
1875
0
            goto error;
1876
0
        }
1877
1878
0
        ksm_state->auth_context = auth_context;
1879
0
        auth_context = NULL;
1880
0
        ksm_state->cksumtype = cksumtype;
1881
1882
0
        *parms->secStateRef = ksm_state;
1883
0
    } else {
1884
1885
        /*
1886
         * We _still_ have to set the secName in process_in_msg().  Do
1887
         * that now with what we were passed in before (we cached it,
1888
         * remember?)
1889
         */
1890
1891
0
        if (entry->secNameLen >= *parms->secNameLen) {
1892
0
            retval = SNMPERR_TOO_LONG;
1893
0
            goto error;
1894
0
        }
1895
0
        memcpy(parms->secName, entry->secName, entry->secNameLen);
1896
0
        parms->secName[entry->secNameLen] = '\0';
1897
0
        *parms->secNameLen = entry->secNameLen;
1898
0
    }
1899
1900
    /*
1901
     * Just in case
1902
     */
1903
1904
0
    parms->secEngineID = null_id;
1905
0
    *parms->secEngineIDLen = 0;
1906
1907
0
    auth_context = NULL;        /* So we don't try to free it on success */
1908
1909
0
  error:
1910
0
    if (retval == SNMPERR_ASN_PARSE_ERR &&
1911
0
        snmp_increment_statistic(STAT_SNMPINASNPARSEERRS) == 0)
1912
0
        DEBUGMSGTL(("ksm", "Failed to increment statistics.\n"));
1913
1914
0
    if (subkey)
1915
0
        krb5_free_keyblock(kcontext, subkey);
1916
1917
#ifdef OLD_HEIMDAL /* OLD_HEIMDAL */
1918
    if (heim_crypto)
1919
      krb5_crypto_destroy(kcontext, heim_crypto);
1920
#endif /* OLD_HEIMDAL */
1921
1922
0
    if (CHECKSUM_CONTENTS(&checksum))
1923
0
        free(CHECKSUM_CONTENTS(&checksum));
1924
1925
0
    if (ivector.data)
1926
0
        free(ivector.data);
1927
1928
0
    if (ticket)
1929
0
        krb5_free_ticket(kcontext, ticket);
1930
1931
0
    if (!response && auth_context)
1932
0
        krb5_auth_con_free(kcontext, auth_context);
1933
1934
0
    if (ap_req.data)
1935
0
        free(ap_req.data);
1936
1937
0
    return retval;
1938
0
}