license changed to LGPL for libnxcl, libnxsnmp, libnxlp, libnxsl, and libnxmap
[public/netxms.git] / src / snmp / libnxsnmp / pdu.cpp
1 /*
2 ** NetXMS - Network Management System
3 ** SNMP support library
4 ** Copyright (C) 2003-2010 Victor Kirhenshtein
5 **
6 ** This program is free software; you can redistribute it and/or modify
7 ** it under the terms of the GNU Lesser General Public License as published by
8 ** the Free Software Foundation; either version 3 of the License, or
9 ** (at your option) any later version.
10 **
11 ** This program is distributed in the hope that it will be useful,
12 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
13 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 ** GNU General Public License for more details.
15 **
16 ** You should have received a copy of the GNU Lesser General Public License
17 ** along with this program; if not, write to the Free Software
18 ** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 **
20 ** File: pdu.cpp
21 **
22 **/
23
24 #include "libnxsnmp.h"
25
26 #ifdef _WITH_ENCRYPTION
27 #ifndef OPENSSL_NO_DES
28 #include <openssl/des.h>
29 #endif
30 #ifndef OPENSSL_NO_AES
31 #include <openssl/aes.h>
32 #endif
33 #endif
34
35
36 //
37 // Static data
38 //
39
40 static BYTE m_hashPlaceholder[12] = { 0xAB, 0xCD, 0xEF, 0xFF, 0x00, 0x11, 0x22, 0x33, 0xBB, 0xAA, 0x99, 0x88 };
41
42
43 //
44 // PDU type to command translation
45 //
46
47 static struct
48 {
49 DWORD dwType;
50 int iVersion;
51 DWORD dwCommand;
52 } PDU_type_to_command[] =
53 {
54 { ASN_TRAP_V1_PDU, SNMP_VERSION_1, SNMP_TRAP },
55 { ASN_TRAP_V2_PDU, SNMP_VERSION_2C, SNMP_TRAP },
56 { ASN_TRAP_V2_PDU, SNMP_VERSION_3, SNMP_TRAP },
57 { ASN_GET_REQUEST_PDU, -1, SNMP_GET_REQUEST },
58 { ASN_GET_NEXT_REQUEST_PDU, -1, SNMP_GET_NEXT_REQUEST },
59 { ASN_SET_REQUEST_PDU, -1, SNMP_SET_REQUEST },
60 { ASN_RESPONSE_PDU, -1, SNMP_RESPONSE },
61 { ASN_REPORT_PDU, -1, SNMP_REPORT },
62 { ASN_INFORM_REQUEST_PDU, -1, SNMP_INFORM_REQUEST },
63 { 0, -1, 0 }
64 };
65
66
67 //
68 // SNMP_PDU default constructor
69 //
70
71 SNMP_PDU::SNMP_PDU()
72 {
73 m_dwVersion = SNMP_VERSION_1;
74 m_dwCommand = SNMP_INVALID_PDU;
75 m_dwNumVariables = 0;
76 m_ppVarList = NULL;
77 m_pEnterprise = NULL;
78 m_dwErrorCode = 0;
79 m_dwErrorIndex = 0;
80 m_dwRqId = 0;
81 m_msgId = 0;
82 m_flags = 0;
83 m_iTrapType = 0;
84 m_iSpecificTrap = 0;
85 m_contextEngineIdLen = 0;
86 m_contextName[0] = 0;
87 m_msgMaxSize = SNMP_DEFAULT_MSG_MAX_SIZE;
88 m_authObject = NULL;
89 }
90
91
92 //
93 // Create request PDU
94 //
95
96 SNMP_PDU::SNMP_PDU(DWORD dwCommand, DWORD dwRqId, DWORD dwVersion)
97 {
98 m_dwVersion = dwVersion;
99 m_dwCommand = dwCommand;
100 m_dwNumVariables = 0;
101 m_ppVarList = NULL;
102 m_pEnterprise = NULL;
103 m_dwErrorCode = 0;
104 m_dwErrorIndex = 0;
105 m_dwRqId = dwRqId;
106 m_msgId = dwRqId;
107 m_flags = 0;
108 m_iTrapType = 0;
109 m_iSpecificTrap = 0;
110 m_contextEngineIdLen = 0;
111 m_contextName[0] = 0;
112 m_msgMaxSize = SNMP_DEFAULT_MSG_MAX_SIZE;
113 m_authObject = NULL;
114 }
115
116
117 //
118 // SNMP_PDU destructor
119 //
120
121 SNMP_PDU::~SNMP_PDU()
122 {
123 DWORD i;
124
125 delete m_pEnterprise;
126 for(i = 0; i < m_dwNumVariables; i++)
127 delete m_ppVarList[i];
128 safe_free(m_ppVarList);
129 safe_free(m_authObject);
130 }
131
132
133 //
134 // Parse single variable binding
135 //
136
137 BOOL SNMP_PDU::parseVariable(BYTE *pData, DWORD dwVarLength)
138 {
139 SNMP_Variable *var;
140 BOOL success = TRUE;
141
142 var = new SNMP_Variable;
143 if (var->Parse(pData, dwVarLength))
144 {
145 bindVariable(var);
146 }
147 else
148 {
149 delete var;
150 success = FALSE;
151 }
152 return success;
153 }
154
155
156 //
157 // Parse variable bindings
158 //
159
160 BOOL SNMP_PDU::parseVarBinds(BYTE *pData, DWORD dwPDULength)
161 {
162 BYTE *pbCurrPos;
163 DWORD dwType, dwLength, dwBindingLength, dwIdLength;
164
165 // Varbind section should be a SEQUENCE
166 if (!BER_DecodeIdentifier(pData, dwPDULength, &dwType, &dwBindingLength, &pbCurrPos, &dwIdLength))
167 return FALSE;
168 if (dwType != ASN_SEQUENCE)
169 return FALSE;
170
171 while(dwBindingLength > 0)
172 {
173 if (!BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
174 return FALSE;
175 if (dwType != ASN_SEQUENCE)
176 return FALSE; // Every binding is a sequence
177 if (dwLength > dwBindingLength)
178 return FALSE; // Invalid length
179
180 if (!parseVariable(pbCurrPos, dwLength))
181 return FALSE;
182 dwBindingLength -= dwLength + dwIdLength;
183 pbCurrPos += dwLength;
184 }
185
186 return TRUE;
187 }
188
189
190 //
191 // Parse generic PDU content
192 //
193
194 BOOL SNMP_PDU::parsePduContent(BYTE *pData, DWORD dwPDULength)
195 {
196 DWORD dwType, dwLength ,dwIdLength;
197 BYTE *pbCurrPos = pData;
198 BOOL bResult = FALSE;
199
200 // Request ID
201 if (BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
202 {
203 if ((dwType == ASN_INTEGER) &&
204 BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)&m_dwRqId))
205 {
206 dwPDULength -= dwLength + dwIdLength;
207 pbCurrPos += dwLength;
208 bResult = TRUE;
209 }
210 }
211
212 // Error code
213 if (bResult)
214 {
215 bResult = FALSE;
216 if (BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
217 {
218 if ((dwType == ASN_INTEGER) &&
219 BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)&m_dwErrorCode))
220 {
221 dwPDULength -= dwLength + dwIdLength;
222 pbCurrPos += dwLength;
223 bResult = TRUE;
224 }
225 }
226 }
227
228 // Error index
229 if (bResult)
230 {
231 bResult = FALSE;
232 if (BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
233 {
234 if ((dwType == ASN_INTEGER) &&
235 BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)&m_dwErrorIndex))
236 {
237 dwPDULength -= dwLength + dwIdLength;
238 pbCurrPos += dwLength;
239 bResult = TRUE;
240 }
241 }
242 }
243
244 if (bResult)
245 bResult = parseVarBinds(pbCurrPos, dwPDULength);
246
247 return bResult;
248 }
249
250
251 //
252 // Parse version 1 TRAP PDU
253 //
254
255 BOOL SNMP_PDU::parseTrapPDU(BYTE *pData, DWORD dwPDULength)
256 {
257 DWORD dwType, dwLength, dwIdLength;
258 BYTE *pbCurrPos = pData;
259 SNMP_OID *oid;
260 DWORD dwBuffer;
261 BOOL bResult = FALSE;
262
263 // Enterprise ID
264 if (BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
265 {
266 if (dwType == ASN_OBJECT_ID)
267 {
268 oid = (SNMP_OID *)malloc(sizeof(SNMP_OID));
269 memset(oid, 0, sizeof(SNMP_OID));
270 if (BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)oid))
271 {
272 m_pEnterprise = new SNMP_ObjectId(oid->dwLength, oid->pdwValue);
273 dwPDULength -= dwLength + dwIdLength;
274 pbCurrPos += dwLength;
275
276 bResult = TRUE;
277 }
278 safe_free(oid->pdwValue);
279 free(oid);
280 }
281 }
282
283 // Agent's address
284 if (bResult)
285 {
286 bResult = FALSE;
287 if (BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
288 {
289 if ((dwType == ASN_IP_ADDR) && (dwLength == 4) &&
290 BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)&m_dwAgentAddr))
291 {
292 dwPDULength -= dwLength + dwIdLength;
293 pbCurrPos += dwLength;
294 bResult = TRUE;
295 }
296 }
297 }
298
299 // Generic trap type
300 if (bResult)
301 {
302 bResult = FALSE;
303 if (BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
304 {
305 if ((dwType == ASN_INTEGER) &&
306 BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)&dwBuffer))
307 {
308 dwPDULength -= dwLength + dwIdLength;
309 pbCurrPos += dwLength;
310 m_iTrapType = (int)dwBuffer;
311 bResult = TRUE;
312 }
313 }
314 }
315
316 // Enterprise trap type
317 if (bResult)
318 {
319 bResult = FALSE;
320 if (BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
321 {
322 if ((dwType == ASN_INTEGER) &&
323 BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)&dwBuffer))
324 {
325 dwPDULength -= dwLength + dwIdLength;
326 pbCurrPos += dwLength;
327 m_iSpecificTrap = (int)dwBuffer;
328 bResult = TRUE;
329 }
330 }
331 }
332
333 // Timestamp
334 if (bResult)
335 {
336 bResult = FALSE;
337 if (BER_DecodeIdentifier(pbCurrPos, dwPDULength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
338 {
339 if ((dwType == ASN_TIMETICKS) &&
340 BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)&m_dwTimeStamp))
341 {
342 dwPDULength -= dwLength + dwIdLength;
343 pbCurrPos += dwLength;
344 bResult = TRUE;
345 }
346 }
347 }
348
349 if (bResult)
350 bResult = parseVarBinds(pbCurrPos, dwPDULength);
351
352 if (bResult)
353 {
354 if (m_iTrapType < 6)
355 {
356 static DWORD pdwStdOid[6][10] =
357 {
358 { 1, 3, 6, 1, 6, 3, 1, 1, 5, 1 }, // cold start
359 { 1, 3, 6, 1, 6, 3, 1, 1, 5, 2 }, // warm start
360 { 1, 3, 6, 1, 6, 3, 1, 1, 5, 3 }, // link down
361 { 1, 3, 6, 1, 6, 3, 1, 1, 5, 4 }, // link up
362 { 1, 3, 6, 1, 6, 3, 1, 1, 5, 5 }, // authentication failure
363 { 1, 3, 6, 1, 6, 3, 1, 1, 5, 6 } // EGP neighbor loss (obsolete)
364 };
365
366 // For standard trap types, create standard V2 Enterprise ID
367 m_pEnterprise->SetValue(pdwStdOid[m_iTrapType], 10);
368 }
369 else
370 {
371 m_pEnterprise->Extend(0);
372 m_pEnterprise->Extend(m_iSpecificTrap);
373 }
374 }
375
376 return bResult;
377 }
378
379
380 //
381 // Parse version 2 TRAP PDU
382 //
383
384 BOOL SNMP_PDU::parseTrap2PDU(BYTE *pData, DWORD dwPDULength)
385 {
386 BOOL bResult;
387 static DWORD pdwStdTrapPrefix[9] = { 1, 3, 6, 1, 6, 3, 1, 1, 5 };
388
389 bResult = parsePduContent(pData, dwPDULength);
390 if (bResult)
391 {
392 bResult = FALSE;
393 if (m_dwNumVariables >= 2)
394 {
395 if (m_ppVarList[1]->GetType() == ASN_OBJECT_ID)
396 {
397 m_pEnterprise = new SNMP_ObjectId(
398 m_ppVarList[1]->GetValueLength() / sizeof(DWORD),
399 (DWORD *)m_ppVarList[1]->GetValue());
400 bResult = TRUE;
401 }
402 }
403
404 // Set V1 trap type and specific trap type fields
405 if (bResult)
406 {
407 if ((m_pEnterprise->Compare(pdwStdTrapPrefix, 9) == OID_SHORTER) &&
408 (m_pEnterprise->Length() == 10))
409 {
410 m_iTrapType = m_pEnterprise->GetValue()[9];
411 m_iSpecificTrap = 0;
412 }
413 else
414 {
415 m_iTrapType = 6;
416 m_iSpecificTrap = m_pEnterprise->GetValue()[m_pEnterprise->Length() - 1];
417 }
418 }
419 }
420 return bResult;
421 }
422
423
424 //
425 // Parse version 3 header
426 //
427
428 BOOL SNMP_PDU::parseV3Header(BYTE *header, DWORD headerLength)
429 {
430 DWORD type, length, idLength, remLength = headerLength;
431 BYTE *currPos = header;
432
433 // Message id
434 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
435 return FALSE;
436 if (type != ASN_INTEGER)
437 return FALSE; // Should be of integer type
438 if (!BER_DecodeContent(type, currPos, length, (BYTE *)&m_msgId))
439 return FALSE; // Error parsing content
440 currPos += length;
441 remLength -= length + idLength;
442
443 // Message max size
444 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
445 return FALSE;
446 if (type != ASN_INTEGER)
447 return FALSE; // Should be of integer type
448 if (!BER_DecodeContent(type, currPos, length, (BYTE *)&m_msgMaxSize))
449 return FALSE; // Error parsing content
450 currPos += length;
451 remLength -= length + idLength;
452
453 // Message flags
454 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
455 return FALSE;
456 if ((type != ASN_OCTET_STRING) || (length != 1))
457 return FALSE;
458 BYTE flags;
459 if (!BER_DecodeContent(type, currPos, length, &flags))
460 return FALSE; // Error parsing content
461 m_flags = flags;
462 currPos += length;
463 remLength -= length + idLength;
464
465 // Security model
466 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
467 return FALSE;
468 if (type != ASN_INTEGER)
469 return FALSE; // Should be of integer type
470 DWORD securityModel;
471 if (!BER_DecodeContent(type, currPos, length, (BYTE *)&securityModel))
472 return FALSE; // Error parsing content
473 currPos += length;
474 remLength -= length + idLength;
475 m_securityModel = (int)securityModel;
476
477 return TRUE;
478 }
479
480
481 //
482 // Parse V3 USM security parameters
483 //
484
485 BOOL SNMP_PDU::parseV3SecurityUsm(BYTE *data, DWORD dataLength)
486 {
487 DWORD type, length, idLength, remLength = dataLength;
488 DWORD engineBoots, engineTime;
489 BYTE *currPos = data;
490 BYTE engineId[SNMP_MAX_ENGINEID_LEN];
491 int engineIdLen;
492
493 // Should be sequence
494 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
495 return FALSE;
496 if (type != ASN_SEQUENCE)
497 return FALSE;
498 remLength = length;
499
500 // Engine ID
501 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
502 return FALSE;
503 if (type != ASN_OCTET_STRING)
504 return FALSE;
505 engineIdLen = length;
506 if (!BER_DecodeContent(type, currPos, length, engineId))
507 return FALSE; // Error parsing content
508 currPos += length;
509 remLength -= length + idLength;
510
511 // engine boots
512 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
513 return FALSE;
514 if (type != ASN_INTEGER)
515 return FALSE;
516 if (!BER_DecodeContent(type, currPos, length, (BYTE *)&engineBoots))
517 return FALSE; // Error parsing content
518 currPos += length;
519 remLength -= length + idLength;
520
521 // engine time
522 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
523 return FALSE;
524 if (type != ASN_INTEGER)
525 return FALSE;
526 if (!BER_DecodeContent(type, currPos, length, (BYTE *)&engineTime))
527 return FALSE; // Error parsing content
528 currPos += length;
529 remLength -= length + idLength;
530
531 m_authoritativeEngine = SNMP_Engine(engineId, engineIdLen, engineBoots, engineTime);
532
533 // User name
534 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
535 return FALSE;
536 if (type != ASN_OCTET_STRING)
537 return FALSE;
538 m_authObject = (char *)malloc(length + 1);
539 if (!BER_DecodeContent(type, currPos, length, (BYTE *)m_authObject))
540 {
541 free(m_authObject);
542 m_authObject = NULL;
543 return FALSE;
544 }
545 m_authObject[length] = 0;
546 currPos += length;
547 remLength -= length + idLength;
548
549 // Message signature
550 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
551 return FALSE;
552 if (type != ASN_OCTET_STRING)
553 return FALSE;
554 memcpy(m_signature, currPos, min(length, 12));
555 memset(currPos, 0, min(length, 12)); // Replace with 0 to generate correct hash in validate method
556 currPos += length;
557 remLength -= length + idLength;
558
559 // Encryption salt
560 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
561 return FALSE;
562 if (type != ASN_OCTET_STRING)
563 return FALSE;
564 memcpy(m_salt, currPos, min(length, 8));
565 currPos += length;
566 remLength -= length + idLength;
567
568 return TRUE;
569 }
570
571
572 //
573 // Parse V3 scoped PDU
574 //
575
576 BOOL SNMP_PDU::parseV3ScopedPdu(BYTE *data, DWORD dataLength)
577 {
578 DWORD type, length, idLength, remLength = dataLength;
579 BYTE *currPos = data;
580
581 // Context engine ID
582 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
583 return FALSE;
584 if ((type != ASN_OCTET_STRING) || (length > SNMP_MAX_ENGINEID_LEN))
585 return FALSE;
586 m_contextEngineIdLen = length;
587 if (!BER_DecodeContent(type, currPos, length, m_contextEngineId))
588 return FALSE; // Error parsing content
589 currPos += length;
590 remLength -= length + idLength;
591
592 // Context name
593 if (!BER_DecodeIdentifier(currPos, remLength, &type, &length, &currPos, &idLength))
594 return FALSE;
595 if ((type != ASN_OCTET_STRING) || (length >= SNMP_MAX_CONTEXT_NAME))
596 return FALSE;
597 if (!BER_DecodeContent(type, currPos, length, (BYTE *)m_contextName))
598 return FALSE; // Error parsing content
599 m_contextName[length] = 0;
600 currPos += length;
601 remLength -= length + idLength;
602
603 return parsePdu(currPos, remLength);
604 }
605
606
607 //
608 // Parse PDU
609 //
610
611 BOOL SNMP_PDU::parsePdu(BYTE *pdu, DWORD pduLength)
612 {
613 BYTE *content;
614 DWORD length, idLength, type;
615 BOOL success;
616
617 if (success = BER_DecodeIdentifier(pdu, pduLength, &type, &length, &content, &idLength))
618 {
619 switch(type)
620 {
621 case ASN_TRAP_V1_PDU:
622 m_dwCommand = SNMP_TRAP;
623 success = parseTrapPDU(content, length);
624 break;
625 case ASN_TRAP_V2_PDU:
626 m_dwCommand = SNMP_TRAP;
627 success = parseTrap2PDU(content, length);
628 break;
629 case ASN_GET_REQUEST_PDU:
630 m_dwCommand = SNMP_GET_REQUEST;
631 success = parsePduContent(content, length);
632 break;
633 case ASN_GET_NEXT_REQUEST_PDU:
634 m_dwCommand = SNMP_GET_NEXT_REQUEST;
635 success = parsePduContent(content, length);
636 break;
637 case ASN_RESPONSE_PDU:
638 m_dwCommand = SNMP_RESPONSE;
639 success = parsePduContent(content, length);
640 break;
641 case ASN_SET_REQUEST_PDU:
642 m_dwCommand = SNMP_SET_REQUEST;
643 success = parsePduContent(content, length);
644 break;
645 case ASN_INFORM_REQUEST_PDU:
646 m_dwCommand = SNMP_INFORM_REQUEST;
647 success = parsePduContent(content, length);
648 break;
649 case ASN_REPORT_PDU:
650 m_dwCommand = SNMP_REPORT;
651 success = parsePduContent(content, length);
652 break;
653 default:
654 success = FALSE;
655 break;
656 }
657 }
658 return success;
659 }
660
661
662 //
663 // Validate V3 signed message
664 //
665
666 BOOL SNMP_PDU::validateSignedMessage(BYTE *msg, DWORD msgLen, SNMP_SecurityContext *securityContext)
667 {
668 BYTE k1[64], k2[64], hash[20], *buffer;
669 int i;
670
671 if (securityContext == NULL)
672 return FALSE; // Unable to validate message without security context
673
674 switch(securityContext->getAuthMethod())
675 {
676 case SNMP_AUTH_MD5:
677 // Create K1 and K2
678 memcpy(k1, securityContext->getAuthKeyMD5(), 16);
679 memset(&k1[16], 0, 48);
680 memcpy(k2, k1, 64);
681 for(i = 0; i < 64; i++)
682 {
683 k1[i] ^= 0x36;
684 k2[i] ^= 0x5C;
685 }
686
687 // Calculate first hash (step 3)
688 buffer = (BYTE *)malloc(msgLen + 64);
689 memcpy(buffer, k1, 64);
690 memcpy(&buffer[64], msg, msgLen);
691 CalculateMD5Hash(buffer, msgLen + 64, hash);
692
693 // Calculate second hash
694 memcpy(buffer, k2, 64);
695 memcpy(&buffer[64], hash, 16);
696 CalculateMD5Hash(buffer, 80, hash);
697 free(buffer);
698 break;
699 case SNMP_AUTH_SHA1:
700 // Create K1 and K2
701 memcpy(k1, securityContext->getAuthKeySHA1(), 20);
702 memset(&k1[20], 0, 44);
703 memcpy(k2, k1, 64);
704 for(i = 0; i < 64; i++)
705 {
706 k1[i] ^= 0x36;
707 k2[i] ^= 0x5C;
708 }
709
710 // Calculate first hash (step 3)
711 buffer = (BYTE *)malloc(msgLen + 64);
712 memcpy(buffer, k1, 64);
713 memcpy(&buffer[64], msg, msgLen);
714 CalculateSHA1Hash(buffer, msgLen + 64, hash);
715
716 // Calculate second hash
717 memcpy(buffer, k2, 64);
718 memcpy(&buffer[64], hash, 20);
719 CalculateSHA1Hash(buffer, 84, hash);
720 free(buffer);
721 break;
722 default:
723 break;
724 }
725
726 // Computed hash should match message signature
727 return !memcmp(m_signature, hash, 12);
728 }
729
730
731 //
732 // Decrypt data in packet
733 //
734
735 BOOL SNMP_PDU::decryptData(BYTE *data, DWORD length, BYTE *decryptedData, SNMP_SecurityContext *securityContext)
736 {
737 #ifdef _WITH_ENCRYPTION
738 if (securityContext == NULL)
739 return FALSE; // Cannot decrypt message without valid security context
740
741 if (securityContext->getPrivMethod() == SNMP_ENCRYPT_DES)
742 {
743 #ifndef OPENSSL_NO_DES
744 if (length % 8 != 0)
745 return FALSE; // Encrypted data length must be an integral multiple of 8
746
747 DES_cblock key;
748 DES_key_schedule schedule;
749 memcpy(&key, securityContext->getPrivKey(), 8);
750 DES_set_key_unchecked(&key, &schedule);
751
752 DES_cblock iv;
753 memcpy(&iv, securityContext->getPrivKey() + 8, 8);
754 for(int i = 0; i < 8; i++)
755 iv[i] ^= m_salt[i];
756
757 DES_ncbc_encrypt(data, decryptedData, length, &schedule, &iv, DES_DECRYPT);
758 #else
759 return FALSE; // Compiled without DES support
760 #endif
761 }
762 else if (securityContext->getPrivMethod() == SNMP_ENCRYPT_AES)
763 {
764 #ifndef OPENSSL_NO_AES
765 AES_KEY key;
766 AES_set_encrypt_key(securityContext->getPrivKey(), 128, &key);
767
768 BYTE iv[16];
769 DWORD boots, engTime;
770 // Use auth. engine from current PDU if possible
771 if ((m_authoritativeEngine.getIdLen() > 0) && (m_authoritativeEngine.getBoots() > 0))
772 {
773 boots = htonl(m_authoritativeEngine.getBoots());
774 engTime = htonl(m_authoritativeEngine.getTime());
775 }
776 else
777 {
778 boots = htonl((DWORD)securityContext->getAuthoritativeEngine().getBoots());
779 engTime = htonl((DWORD)securityContext->getAuthoritativeEngine().getTime());
780 }
781 memcpy(iv, &boots, 4);
782 memcpy(&iv[4], &engTime, 4);
783 memcpy(&iv[8], m_salt, 8);
784
785 int num = 0;
786 AES_cfb128_encrypt(data, decryptedData, length, &key, iv, &num, AES_DECRYPT);
787 #else
788 return FALSE; // Compiled without AES support
789 #endif
790 }
791 else
792 {
793 return FALSE;
794 }
795 return TRUE;
796 #else
797 return FALSE; // No encryption support
798 #endif
799 }
800
801
802 //
803 // Create PDU from packet
804 //
805
806 BOOL SNMP_PDU::parse(BYTE *pRawData, DWORD dwRawLength, SNMP_SecurityContext *securityContext, bool engineIdAutoupdate)
807 {
808 BYTE *pbCurrPos;
809 DWORD dwType, dwLength, dwPacketLength, dwIdLength;
810 BOOL bResult = FALSE;
811
812 // Packet start
813 if (!BER_DecodeIdentifier(pRawData, dwRawLength, &dwType, &dwPacketLength, &pbCurrPos, &dwIdLength))
814 return FALSE;
815 if (dwType != ASN_SEQUENCE)
816 return FALSE; // Packet should start with SEQUENCE
817
818 // Version
819 if (!BER_DecodeIdentifier(pbCurrPos, dwPacketLength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
820 return FALSE;
821 if (dwType != ASN_INTEGER)
822 return FALSE; // Version field should be of integer type
823 if (!BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)&m_dwVersion))
824 return FALSE; // Error parsing content of version field
825 pbCurrPos += dwLength;
826 dwPacketLength -= dwLength + dwIdLength;
827 if ((m_dwVersion != SNMP_VERSION_1) && (m_dwVersion != SNMP_VERSION_2C) && ((m_dwVersion != SNMP_VERSION_3)))
828 return FALSE; // Unsupported SNMP version
829
830 if (m_dwVersion == SNMP_VERSION_3)
831 {
832 // V3 header
833 if (!BER_DecodeIdentifier(pbCurrPos, dwPacketLength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
834 return FALSE;
835 if (dwType != ASN_SEQUENCE)
836 return FALSE; // Should be sequence
837
838 // We don't need BER_DecodeContent because sequence does not need any special decoding
839 if (!parseV3Header(pbCurrPos, dwLength))
840 return FALSE;
841 pbCurrPos += dwLength;
842 dwPacketLength -= dwLength + dwIdLength;
843
844 // Security parameters
845 if (!BER_DecodeIdentifier(pbCurrPos, dwPacketLength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
846 return FALSE;
847 if (dwType != ASN_OCTET_STRING)
848 return FALSE; // Should be octet string
849
850 if (m_securityModel == SNMP_SECURITY_MODEL_USM)
851 {
852 if (!parseV3SecurityUsm(pbCurrPos, dwLength))
853 return FALSE;
854
855 if (engineIdAutoupdate && (m_authoritativeEngine.getIdLen() > 0) && (securityContext != NULL))
856 {
857 securityContext->setAuthoritativeEngine(m_authoritativeEngine);
858 }
859
860 if (m_flags & SNMP_AUTH_FLAG)
861 {
862 if (!validateSignedMessage(pRawData, dwRawLength, securityContext))
863 return FALSE;
864 }
865 }
866
867 pbCurrPos += dwLength;
868 dwPacketLength -= dwLength + dwIdLength;
869
870 // Decrypt scoped PDU if needed
871 if ((m_securityModel == SNMP_SECURITY_MODEL_USM) && (m_flags & SNMP_PRIV_FLAG))
872 {
873 BYTE *scopedPduStart = pbCurrPos;
874
875 if (!BER_DecodeIdentifier(pbCurrPos, dwPacketLength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
876 return FALSE;
877 if (dwType != ASN_OCTET_STRING)
878 return FALSE; // Should be encoded as octet string
879
880 BYTE *decryptedPdu = (BYTE *)malloc(dwLength);
881 if (!decryptData(pbCurrPos, dwLength, decryptedPdu, securityContext))
882 {
883 free(decryptedPdu);
884 return FALSE;
885 }
886
887 pbCurrPos = scopedPduStart;
888 memcpy(pbCurrPos, decryptedPdu, dwLength);
889 free(decryptedPdu);
890 }
891
892 // Scoped PDU
893 if (!BER_DecodeIdentifier(pbCurrPos, dwPacketLength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
894 return FALSE;
895 if (dwType != ASN_SEQUENCE)
896 return FALSE; // Should be sequence
897 bResult = parseV3ScopedPdu(pbCurrPos, dwLength);
898 }
899 else
900 {
901 // Community string
902 if (!BER_DecodeIdentifier(pbCurrPos, dwPacketLength, &dwType, &dwLength, &pbCurrPos, &dwIdLength))
903 return FALSE;
904 if (dwType != ASN_OCTET_STRING)
905 return FALSE; // Community field should be of string type
906 m_authObject = (char *)malloc(dwLength + 1);
907 if (!BER_DecodeContent(dwType, pbCurrPos, dwLength, (BYTE *)m_authObject))
908 {
909 free(m_authObject);
910 m_authObject = NULL;
911 return FALSE; // Error parsing content of version field
912 }
913 m_authObject[dwLength] = 0;
914 pbCurrPos += dwLength;
915 dwPacketLength -= dwLength + dwIdLength;
916
917 bResult = parsePdu(pbCurrPos, dwLength);
918 }
919
920 return bResult;
921 }
922
923
924 //
925 // Create packet from PDU
926 //
927
928 DWORD SNMP_PDU::encode(BYTE **ppBuffer, SNMP_SecurityContext *securityContext)
929 {
930 DWORD i, dwBufferSize, dwBytes, dwVarBindsSize, dwPDUType, dwPDUSize, dwPacketSize, dwValue;
931 BYTE *pbCurrPos, *pBlock, *pVarBinds, *pPacket;
932
933 // Estimate required buffer size and allocate it
934 for(dwBufferSize = 1024, i = 0; i < m_dwNumVariables; i++)
935 dwBufferSize += m_ppVarList[i]->GetValueLength() + m_ppVarList[i]->GetName()->Length() * 4 + 16;
936 pBlock = (BYTE *)malloc(dwBufferSize);
937 pVarBinds = (BYTE *)malloc(dwBufferSize);
938 pPacket = (BYTE *)malloc(dwBufferSize);
939
940 // Encode variables
941 for(i = 0, dwVarBindsSize = 0, pbCurrPos = pVarBinds; i < m_dwNumVariables; i++)
942 {
943 dwBytes = m_ppVarList[i]->Encode(pbCurrPos, dwBufferSize - dwVarBindsSize);
944 pbCurrPos += dwBytes;
945 dwVarBindsSize += dwBytes;
946 }
947
948 // Determine PDU type
949 for(i = 0; PDU_type_to_command[i].dwType != 0; i++)
950 if (((m_dwVersion == (DWORD)PDU_type_to_command[i].iVersion) ||
951 (PDU_type_to_command[i].iVersion == -1)) &&
952 (PDU_type_to_command[i].dwCommand == m_dwCommand))
953 {
954 dwPDUType = PDU_type_to_command[i].dwType;
955 break;
956 }
957
958 // Encode PDU header
959 if (dwPDUType != 0)
960 {
961 pbCurrPos = pBlock;
962 dwPDUSize = 0;
963 switch(dwPDUType)
964 {
965 case ASN_TRAP_V1_PDU:
966 dwBytes = BER_Encode(ASN_OBJECT_ID, (BYTE *)m_pEnterprise->GetValue(),
967 m_pEnterprise->Length() * sizeof(DWORD),
968 pbCurrPos, dwBufferSize - dwPDUSize);
969 dwPDUSize += dwBytes;
970 pbCurrPos += dwBytes;
971
972 dwBytes = BER_Encode(ASN_IP_ADDR, (BYTE *)&m_dwAgentAddr, sizeof(DWORD),
973 pbCurrPos, dwBufferSize - dwPDUSize);
974 dwPDUSize += dwBytes;
975 pbCurrPos += dwBytes;
976
977 dwValue = (DWORD)m_iTrapType;
978 dwBytes = BER_Encode(ASN_INTEGER, (BYTE *)&dwValue, sizeof(DWORD),
979 pbCurrPos, dwBufferSize - dwPDUSize);
980 dwPDUSize += dwBytes;
981 pbCurrPos += dwBytes;
982
983 dwValue = (DWORD)m_iSpecificTrap;
984 dwBytes = BER_Encode(ASN_INTEGER, (BYTE *)&dwValue, sizeof(DWORD),
985 pbCurrPos, dwBufferSize - dwPDUSize);
986 dwPDUSize += dwBytes;
987 pbCurrPos += dwBytes;
988
989 dwBytes = BER_Encode(ASN_INTEGER, (BYTE *)&m_dwTimeStamp, sizeof(DWORD),
990 pbCurrPos, dwBufferSize - dwPDUSize);
991 dwPDUSize += dwBytes;
992 pbCurrPos += dwBytes;
993 break;
994 default:
995 dwBytes = BER_Encode(ASN_INTEGER, (BYTE *)&m_dwRqId, sizeof(DWORD),
996 pbCurrPos, dwBufferSize - dwPDUSize);
997 dwPDUSize += dwBytes;
998 pbCurrPos += dwBytes;
999
1000 dwBytes = BER_Encode(ASN_INTEGER, (BYTE *)&m_dwErrorCode, sizeof(DWORD),
1001 pbCurrPos, dwBufferSize - dwPDUSize);
1002 dwPDUSize += dwBytes;
1003 pbCurrPos += dwBytes;
1004
1005 dwBytes = BER_Encode(ASN_INTEGER, (BYTE *)&m_dwErrorIndex, sizeof(DWORD),
1006 pbCurrPos, dwBufferSize - dwPDUSize);
1007 dwPDUSize += dwBytes;
1008 pbCurrPos += dwBytes;
1009 break;
1010 }
1011
1012 // Encode varbinds into PDU
1013 if ((m_dwVersion != SNMP_VERSION_3) || ((securityContext != NULL) && (securityContext->getAuthoritativeEngine().getIdLen() != 0)))
1014 {
1015 dwBytes = BER_Encode(ASN_SEQUENCE, pVarBinds, dwVarBindsSize,
1016 pbCurrPos, dwBufferSize - dwPDUSize);
1017 }
1018 else
1019 {
1020 // Do not encode varbinds into engine id discovery message
1021 dwBytes = BER_Encode(ASN_SEQUENCE, NULL, 0, pbCurrPos, dwBufferSize - dwPDUSize);
1022 }
1023 dwPDUSize += dwBytes;
1024
1025 // Encode packet header
1026 pbCurrPos = pPacket;
1027 dwPacketSize = 0;
1028
1029 dwBytes = BER_Encode(ASN_INTEGER, (BYTE *)&m_dwVersion, sizeof(DWORD),
1030 pbCurrPos, dwBufferSize);
1031 dwPacketSize += dwBytes;
1032 pbCurrPos += dwBytes;
1033
1034 if (m_dwVersion == SNMP_VERSION_3)
1035 {
1036 // Generate encryption salt if packet has to be encrypted
1037 if (securityContext->needEncryption())
1038 {
1039 QWORD temp = htonq(GetCurrentTimeMs());
1040 memcpy(m_salt, &temp, 8);
1041 }
1042
1043 dwBytes = encodeV3Header(pbCurrPos, dwBufferSize - dwPacketSize, securityContext);
1044 dwPacketSize += dwBytes;
1045 pbCurrPos += dwBytes;
1046
1047 dwBytes = encodeV3SecurityParameters(pbCurrPos, dwBufferSize - dwPacketSize, securityContext);
1048 dwPacketSize += dwBytes;
1049 pbCurrPos += dwBytes;
1050
1051 dwBytes = encodeV3ScopedPDU(dwPDUType, pBlock, dwPDUSize, pbCurrPos, dwBufferSize - dwPacketSize);
1052 if (securityContext->needEncryption())
1053 {
1054 #ifdef _WITH_ENCRYPTION
1055 if (securityContext->getPrivMethod() == SNMP_ENCRYPT_DES)
1056 {
1057 #ifndef OPENSSL_NO_DES
1058 DWORD encSize = (dwBytes % 8 == 0) ? dwBytes : (dwBytes + (8 - (dwBytes % 8)));
1059 BYTE *encryptedPdu = (BYTE *)malloc(encSize);
1060
1061 DES_cblock key;
1062 DES_key_schedule schedule;
1063 memcpy(&key, securityContext->getPrivKey(), 8);
1064 DES_set_key_unchecked(&key, &schedule);
1065
1066 DES_cblock iv;
1067 memcpy(&iv, securityContext->getPrivKey() + 8, 8);
1068 for(int i = 0; i < 8; i++)
1069 iv[i] ^= m_salt[i];
1070
1071 DES_ncbc_encrypt(pbCurrPos, encryptedPdu, dwBytes, &schedule, &iv, DES_ENCRYPT);
1072 dwBytes = BER_Encode(ASN_OCTET_STRING, encryptedPdu, encSize, pbCurrPos, dwBufferSize - dwPacketSize);
1073 free(encryptedPdu);
1074 #else
1075 dwBytes = 0; // Error - no DES support
1076 goto cleanup;
1077 #endif
1078 }
1079 else if (securityContext->getPrivMethod() == SNMP_ENCRYPT_AES)
1080 {
1081 #ifndef OPENSSL_NO_AES
1082 AES_KEY key;
1083 AES_set_encrypt_key(securityContext->getPrivKey(), 128, &key);
1084
1085 BYTE iv[16];
1086 DWORD boots = htonl((DWORD)securityContext->getAuthoritativeEngine().getBoots());
1087 DWORD engTime = htonl((DWORD)securityContext->getAuthoritativeEngine().getTime());
1088 memcpy(iv, &boots, 4);
1089 memcpy(&iv[4], &engTime, 4);
1090 memcpy(&iv[8], m_salt, 8);
1091
1092 BYTE *encryptedPdu = (BYTE *)malloc(dwBytes);
1093 int num = 0;
1094 AES_cfb128_encrypt(pbCurrPos, encryptedPdu, dwBytes, &key, iv, &num, AES_ENCRYPT);
1095 dwBytes = BER_Encode(ASN_OCTET_STRING, encryptedPdu, dwBytes, pbCurrPos, dwBufferSize - dwPacketSize);
1096 free(encryptedPdu);
1097 #else
1098 dwBytes = 0; // Error - no AES support
1099 goto cleanup;
1100 #endif
1101 }
1102 else
1103 {
1104 dwBytes = 0; // Error - unsupported method
1105 goto cleanup;
1106 }
1107 #else
1108 dwBytes = 0; // Error
1109 goto cleanup;
1110 #endif
1111 }
1112 dwPacketSize += dwBytes;
1113 }
1114 else
1115 {
1116 dwBytes = BER_Encode(ASN_OCTET_STRING, (BYTE *)securityContext->getCommunity(),
1117 (DWORD)strlen(securityContext->getCommunity()), pbCurrPos,
1118 dwBufferSize - dwPacketSize);
1119 dwPacketSize += dwBytes;
1120 pbCurrPos += dwBytes;
1121
1122 // Encode PDU into packet
1123 dwBytes = BER_Encode(dwPDUType, pBlock, dwPDUSize, pbCurrPos, dwBufferSize - dwPacketSize);
1124 dwPacketSize += dwBytes;
1125 }
1126
1127 // And final step: allocate buffer for entire datagramm and wrap packet
1128 // into SEQUENCE
1129 *ppBuffer = (BYTE *)malloc(dwPacketSize + 6);
1130 dwBytes = BER_Encode(ASN_SEQUENCE, pPacket, dwPacketSize, *ppBuffer, dwPacketSize + 6);
1131
1132 // Sign message
1133 if ((m_dwVersion == SNMP_VERSION_3) && securityContext->needAuthentication())
1134 {
1135 signMessage(*ppBuffer, dwBytes, securityContext);
1136 }
1137 }
1138 else
1139 {
1140 dwBytes = 0; // Error
1141 }
1142
1143 cleanup:
1144 free(pPacket);
1145 free(pBlock);
1146 free(pVarBinds);
1147 return dwBytes;
1148 }
1149
1150
1151 //
1152 // Encode version 3 header
1153 //
1154
1155 DWORD SNMP_PDU::encodeV3Header(BYTE *buffer, DWORD bufferSize, SNMP_SecurityContext *securityContext)
1156 {
1157 BYTE header[256];
1158 DWORD bytes, securityModel = securityContext->getSecurityModel();
1159
1160 BYTE flags = SNMP_REPORTABLE_FLAG;
1161 if (securityContext->getAuthoritativeEngine().getIdLen() != 0)
1162 {
1163 if (securityContext->needAuthentication())
1164 {
1165 flags |= SNMP_AUTH_FLAG;
1166 if (securityContext->needEncryption())
1167 {
1168 flags |= SNMP_PRIV_FLAG;
1169 }
1170 }
1171 }
1172
1173 bytes = BER_Encode(ASN_INTEGER, (BYTE *)&m_dwRqId, sizeof(DWORD), header, 256);
1174 bytes += BER_Encode(ASN_INTEGER, (BYTE *)&m_msgMaxSize, sizeof(DWORD), &header[bytes], 256 - bytes);
1175 bytes += BER_Encode(ASN_OCTET_STRING, &flags, 1, &header[bytes], 256 - bytes);
1176 bytes += BER_Encode(ASN_INTEGER, (BYTE *)&securityModel, sizeof(DWORD), &header[bytes], 256 - bytes);
1177 return BER_Encode(ASN_SEQUENCE, header, bytes, buffer, bufferSize);
1178 }
1179
1180
1181 //
1182 // Encode version 3 security parameters
1183 //
1184
1185 DWORD SNMP_PDU::encodeV3SecurityParameters(BYTE *buffer, DWORD bufferSize, SNMP_SecurityContext *securityContext)
1186 {
1187 BYTE securityParameters[1024], sequence[1040];
1188 DWORD bytes;
1189 DWORD engineBoots = securityContext->getAuthoritativeEngine().getBoots();
1190 DWORD engineTime = securityContext->getAuthoritativeEngine().getTime();
1191
1192 if ((securityContext != NULL) && (securityContext->getSecurityModel() == SNMP_SECURITY_MODEL_USM))
1193 {
1194 bytes = BER_Encode(ASN_OCTET_STRING, securityContext->getAuthoritativeEngine().getId(),
1195 securityContext->getAuthoritativeEngine().getIdLen(), securityParameters, 1024);
1196 bytes += BER_Encode(ASN_INTEGER, (BYTE *)&engineBoots, sizeof(DWORD), &securityParameters[bytes], 1024 - bytes);
1197 bytes += BER_Encode(ASN_INTEGER, (BYTE *)&engineTime, sizeof(DWORD), &securityParameters[bytes], 1024 - bytes);
1198
1199 // Don't send user and auth/priv parameters in engine id discovery message
1200 if (securityContext->getAuthoritativeEngine().getIdLen() != 0)
1201 {
1202 bytes += BER_Encode(ASN_OCTET_STRING, (BYTE *)securityContext->getUser(), (DWORD)strlen(securityContext->getUser()), &securityParameters[bytes], 1024 - bytes);
1203
1204 // Authentication parameters
1205 if (securityContext->needAuthentication())
1206 {
1207 // Add placeholder for message hash
1208 bytes += BER_Encode(ASN_OCTET_STRING, m_hashPlaceholder, 12, &securityParameters[bytes], 1024 - bytes);
1209 }
1210 else
1211 {
1212 bytes += BER_Encode(ASN_OCTET_STRING, NULL, 0, &securityParameters[bytes], 1024 - bytes);
1213 }
1214
1215 // Privacy parameters
1216 if (securityContext->needEncryption())
1217 {
1218 bytes += BER_Encode(ASN_OCTET_STRING, m_salt, 8, &securityParameters[bytes], 1024 - bytes);
1219 }
1220 else
1221 {
1222 bytes += BER_Encode(ASN_OCTET_STRING, NULL, 0, &securityParameters[bytes], 1024 - bytes);
1223 }
1224 }
1225 else
1226 {
1227 // Empty strings in place of user name, auth parameters and privacy parameters
1228 bytes += BER_Encode(ASN_OCTET_STRING, NULL, 0, &securityParameters[bytes], 1024 - bytes);
1229 bytes += BER_Encode(ASN_OCTET_STRING, NULL, 0, &securityParameters[bytes], 1024 - bytes);
1230 bytes += BER_Encode(ASN_OCTET_STRING, NULL, 0, &securityParameters[bytes], 1024 - bytes);
1231 }
1232
1233 // Wrap into sequence
1234 bytes = BER_Encode(ASN_SEQUENCE, securityParameters, bytes, sequence, 1040);
1235
1236 // Wrap sequence into octet string
1237 bytes = BER_Encode(ASN_OCTET_STRING, sequence, bytes, buffer, bufferSize);
1238 }
1239 else
1240 {
1241 bytes = BER_Encode(ASN_OCTET_STRING, NULL, 0, buffer, bufferSize);
1242 }
1243 return bytes;
1244 }
1245
1246
1247 //
1248 // Encode versionj 3 scoped PDU
1249 //
1250
1251 DWORD SNMP_PDU::encodeV3ScopedPDU(DWORD pduType, BYTE *pdu, DWORD pduSize, BYTE *buffer, DWORD bufferSize)
1252 {
1253 DWORD spduLen = pduSize + SNMP_MAX_CONTEXT_NAME + SNMP_MAX_ENGINEID_LEN + 32;
1254 BYTE *spdu = (BYTE *)malloc(spduLen);
1255 DWORD bytes;
1256
1257 bytes = BER_Encode(ASN_OCTET_STRING, m_contextEngineId, (DWORD)m_contextEngineIdLen, spdu, spduLen);
1258 bytes += BER_Encode(ASN_OCTET_STRING, (BYTE *)m_contextName, (DWORD)strlen(m_contextName), &spdu[bytes], spduLen - bytes);
1259 bytes += BER_Encode(pduType, pdu, pduSize, &spdu[bytes], spduLen - bytes);
1260
1261 // Wrap scoped PDU into SEQUENCE
1262 bytes = BER_Encode(ASN_SEQUENCE, spdu, bytes, buffer, bufferSize);
1263 free(spdu);
1264 return bytes;
1265 }
1266
1267
1268 //
1269 // Sign message
1270 //
1271 // Algorithm described in RFC:
1272 // 1) The msgAuthenticationParameters field is set to the serialization,
1273 // according to the rules in [RFC3417], of an OCTET STRING containing
1274 // 12 zero octets.
1275 // 2) From the secret authKey, two keys K1 and K2 are derived:
1276 // a) extend the authKey to 64 octets by appending 48 zero octets;
1277 // save it as extendedAuthKey
1278 // b) obtain IPAD by replicating the octet 0x36 64 times;
1279 // c) obtain K1 by XORing extendedAuthKey with IPAD;
1280 // d) obtain OPAD by replicating the octet 0x5C 64 times;
1281 // e) obtain K2 by XORing extendedAuthKey with OPAD.
1282 // 3) Prepend K1 to the wholeMsg and calculate MD5 digest over it
1283 // according to [RFC1321].
1284 // 4) Prepend K2 to the result of the step 4 and calculate MD5 digest
1285 // over it according to [RFC1321]. Take the first 12 octets of the
1286 // final digest - this is Message Authentication Code (MAC).
1287 // 5) Replace the msgAuthenticationParameters field with MAC obtained in
1288 // the step 4.
1289 //
1290
1291 void SNMP_PDU::signMessage(BYTE *msg, DWORD msgLen, SNMP_SecurityContext *securityContext)
1292 {
1293 int i, hashPos;
1294
1295 // Find placeholder for hash
1296 for(hashPos = 0; hashPos < (int)msgLen - 12; hashPos++)
1297 if (!memcmp(&msg[hashPos], m_hashPlaceholder, 12))
1298 break;
1299
1300 // Fill hash placeholder with zeroes
1301 memset(&msg[hashPos], 0, 12);
1302
1303 BYTE k1[64], k2[64], hash[20], *buffer;
1304 switch(securityContext->getAuthMethod())
1305 {
1306 case SNMP_AUTH_MD5:
1307 // Create K1 and K2
1308 memcpy(k1, securityContext->getAuthKeyMD5(), 16);
1309 memset(&k1[16], 0, 48);
1310 memcpy(k2, k1, 64);
1311 for(i = 0; i < 64; i++)
1312 {
1313 k1[i] ^= 0x36;
1314 k2[i] ^= 0x5C;
1315 }
1316
1317 // Calculate first hash (step 3)
1318 buffer = (BYTE *)malloc(msgLen + 64);
1319 memcpy(buffer, k1, 64);
1320 memcpy(&buffer[64], msg, msgLen);
1321 CalculateMD5Hash(buffer, msgLen + 64, hash);
1322
1323 // Calculate second hash
1324 memcpy(buffer, k2, 64);
1325 memcpy(&buffer[64], hash, 16);
1326 CalculateMD5Hash(buffer, 80, hash);
1327 free(buffer);
1328 break;
1329 case SNMP_AUTH_SHA1:
1330 // Create K1 and K2
1331 memcpy(k1, securityContext->getAuthKeySHA1(), 20);
1332 memset(&k1[20], 0, 44);
1333 memcpy(k2, k1, 64);
1334 for(i = 0; i < 64; i++)
1335 {
1336 k1[i] ^= 0x36;
1337 k2[i] ^= 0x5C;
1338 }
1339
1340 // Calculate first hash (step 3)
1341 buffer = (BYTE *)malloc(msgLen + 64);
1342 memcpy(buffer, k1, 64);
1343 memcpy(&buffer[64], msg, msgLen);
1344 CalculateSHA1Hash(buffer, msgLen + 64, hash);
1345
1346 // Calculate second hash
1347 memcpy(buffer, k2, 64);
1348 memcpy(&buffer[64], hash, 20);
1349 CalculateSHA1Hash(buffer, 84, hash);
1350 free(buffer);
1351 break;
1352 default:
1353 break;
1354 }
1355
1356 // Update message hash
1357 memcpy(&msg[hashPos], hash, 12);
1358 }
1359
1360
1361 //
1362 // Bind variable to PDU
1363 //
1364
1365 void SNMP_PDU::bindVariable(SNMP_Variable *pVar)
1366 {
1367 m_ppVarList = (SNMP_Variable **)realloc(m_ppVarList, sizeof(SNMP_Variable *) * (m_dwNumVariables + 1));
1368 m_ppVarList[m_dwNumVariables] = pVar;
1369 m_dwNumVariables++;
1370 }
1371
1372
1373 //
1374 // Set context ID
1375 //
1376
1377 void SNMP_PDU::setContextEngineId(BYTE *id, int len)
1378 {
1379 m_contextEngineIdLen = min(len, SNMP_MAX_ENGINEID_LEN);
1380 memcpy(m_contextEngineId, id, m_contextEngineIdLen);
1381 }
1382
1383 void SNMP_PDU::setContextEngineId(const char *id)
1384 {
1385 m_contextEngineIdLen = min((int)strlen(id), SNMP_MAX_ENGINEID_LEN);
1386 memcpy(m_contextEngineId, id, m_contextEngineIdLen);
1387 }
1388
1389
1390 //
1391 // Set context name
1392 //
1393
1394 void SNMP_PDU::setContextName(const char *name)
1395 {
1396 strncpy(m_contextName, name, SNMP_MAX_CONTEXT_NAME);
1397 m_contextName[SNMP_MAX_CONTEXT_NAME - 1] = 0;
1398 }