Coverage Report

Created: 2024-07-27 06:05

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