/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 | } |