USM.pm 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898
  1. # -*- mode: perl -*-
  2. # ============================================================================
  3. package Net::SNMP::Security::USM;
  4. # $Id: USM.pm,v 4.1 2010/09/10 00:01:22 dtown Rel $
  5. # Object that implements the SNMPv3 User-based Security Model.
  6. # Copyright (c) 2001-2010 David M. Town <dtown@cpan.org>
  7. # All rights reserved.
  8. # This program is free software; you may redistribute it and/or modify it
  9. # under the same terms as the Perl 5 programming language system itself.
  10. # ============================================================================
  11. use strict;
  12. use Net::SNMP::Security qw( :ALL );
  13. use Net::SNMP::Message qw(
  14. :msgFlags asn1_itoa OCTET_STRING SEQUENCE INTEGER SNMP_VERSION_3 TRUE FALSE
  15. );
  16. use Crypt::DES();
  17. use Digest::MD5();
  18. use Digest::SHA qw( hmac_sha1 hmac_sha224 hmac_sha256 hmac_sha384 hmac_sha512 );
  19. use Digest::HMAC_MD5 qw ( hmac_md5 );
  20. ## Version of the Net::SNMP::Security::USM module
  21. our $VERSION = v4.0.1;
  22. ## Handle importing/exporting of symbols
  23. use base qw( Net::SNMP::Security );
  24. our @EXPORT_OK;
  25. our %EXPORT_TAGS = (
  26. authprotos => [
  27. qw( AUTH_PROTOCOL_NONE AUTH_PROTOCOL_HMACMD5 AUTH_PROTOCOL_HMACSHA
  28. AUTH_PROTOCOL_HMACSHA224 AUTH_PROTOCOL_HMACSHA256.
  29. AUTH_PROTOCOL_HMACSHA384 AUTH_PROTOCOL_HMACSHA512 )
  30. ],
  31. levels => [
  32. qw( SECURITY_LEVEL_NOAUTHNOPRIV SECURITY_LEVEL_AUTHNOPRIV
  33. SECURITY_LEVEL_AUTHPRIV )
  34. ],
  35. models => [
  36. qw( SECURITY_MODEL_ANY SECURITY_MODEL_SNMPV1 SECURITY_MODEL_SNMPV2C
  37. SECURITY_MODEL_USM )
  38. ],
  39. privprotos => [
  40. qw( PRIV_PROTOCOL_NONE PRIV_PROTOCOL_DES PRIV_PROTOCOL_AESCFB128
  41. PRIV_PROTOCOL_DRAFT_3DESEDE PRIV_PROTOCOL_DRAFT_AESCFB128
  42. PRIV_PROTOCOL_DRAFT_AESCFB192 PRIV_PROTOCOL_DRAFT_AESCFB256 )
  43. ],
  44. );
  45. Exporter::export_ok_tags( qw( authprotos levels models privprotos ) );
  46. $EXPORT_TAGS{ALL} = [ @EXPORT_OK ];
  47. ## RCC 3414 - Authentication protocols
  48. sub AUTH_PROTOCOL_NONE { '1.3.6.1.6.3.10.1.1.1' } # usmNoAuthProtocol
  49. sub AUTH_PROTOCOL_HMACMD5 { '1.3.6.1.6.3.10.1.1.2' } # usmHMACMD5AuthProtocol
  50. sub AUTH_PROTOCOL_HMACSHA { '1.3.6.1.6.3.10.1.1.3' } # usmHMACSHAAuthProtocol
  51. sub AUTH_PROTOCOL_HMACSHA224 { '1.3.6.1.6.3.10.1.1.4' } # usmHMAC128SHA224AuthProtocol
  52. sub AUTH_PROTOCOL_HMACSHA256 { '1.3.6.1.6.3.10.1.1.5' } # usmHMAC192SHA256AuthProtocol
  53. sub AUTH_PROTOCOL_HMACSHA384 { '1.3.6.1.6.3.10.1.1.6' } # usmHMAC256SHA384AuthProtocol
  54. sub AUTH_PROTOCOL_HMACSHA512 { '1.3.6.1.6.3.10.1.1.7' } # usmHMAC384SHA512AuthProtocol
  55. ## RFC 3414 - Privacy protocols
  56. sub PRIV_PROTOCOL_NONE { '1.3.6.1.6.3.10.1.2.1' } # usmNoPrivProtocol
  57. sub PRIV_PROTOCOL_DES { '1.3.6.1.6.3.10.1.2.2' } # usmDESPrivProtocol
  58. ## RFC 3826 - The AES Cipher Algorithm in the SNMP USM
  59. # usmAesCfb128Protocol
  60. sub PRIV_PROTOCOL_AESCFB128 { '1.3.6.1.6.3.10.1.2.4' }
  61. # The privacy protocols below have been implemented using the draft
  62. # specifications intended to extend the User-based Security Model
  63. # defined in RFC 3414. Since the object definitions have not been
  64. # standardized, they have been based on the Extended Security Options
  65. # Consortium MIB found at http://www.snmp.com/eso/esoConsortiumMIB.txt.
  66. # Extension to Support Triple-DES EDE <draft-reeder-snmpv3-usm-3desede-00.txt>
  67. # Reeder and Gudmunsson; October 1999, expired April 2000
  68. # usm3DESPrivProtocol
  69. sub PRIV_PROTOCOL_DRAFT_3DESEDE { '1.3.6.1.4.1.14832.1.1' }
  70. # AES Cipher Algorithm in the USM <draft-blumenthal-aes-usm-04.txt>
  71. # Blumenthal, Maino, and McCloghrie; October 2002, expired April 2003
  72. # usmAESCfb128PrivProtocol
  73. sub PRIV_PROTOCOL_DRAFT_AESCFB128 { '1.3.6.1.4.1.14832.1.2' }
  74. # usmAESCfb192PrivProtocol
  75. sub PRIV_PROTOCOL_DRAFT_AESCFB192 { '1.3.6.1.4.1.14832.1.3' }
  76. # usmAESCfb256PrivProtocol
  77. sub PRIV_PROTOCOL_DRAFT_AESCFB256 { '1.3.6.1.4.1.14832.1.4' }
  78. ## Package variables
  79. our $ENGINE_ID; # Our authoritative snmpEngineID
  80. # [public methods] -----------------------------------------------------------
  81. sub new
  82. {
  83. my ($class, %argv) = @_;
  84. # Create a new data structure for the object
  85. my $this = bless {
  86. '_error' => undef, # Error message
  87. '_version' => SNMP_VERSION_3, # version
  88. '_authoritative' => FALSE, # Authoritative flag
  89. '_discovered' => FALSE, # Engine discovery flag
  90. '_synchronized' => FALSE, # Synchronization flag
  91. '_engine_id' => q{}, # snmpEngineID
  92. '_engine_boots' => 0, # snmpEngineBoots
  93. '_engine_time' => 0, # snmpEngineTime
  94. '_latest_engine_time' => 0, # latestReceivedEngineTime
  95. '_time_epoc' => time(), # snmpEngineBoots epoc
  96. '_user_name' => q{}, # securityName
  97. '_auth_data' => undef, # Authentication data
  98. '_auth_maclen' => undef, # MAC length
  99. '_auth_key' => undef, # authKey
  100. '_auth_password' => undef, # Authentication password
  101. '_auth_protocol' => AUTH_PROTOCOL_HMACMD5, # authProtocol
  102. '_priv_data' => undef, # Privacy data
  103. '_priv_key' => undef, # privKey
  104. '_priv_password' => undef, # Privacy password
  105. '_priv_protocol' => PRIV_PROTOCOL_DES, # privProtocol
  106. '_security_level' => SECURITY_LEVEL_NOAUTHNOPRIV
  107. }, $class;
  108. # We first need to find out if we are an authoritative SNMP
  109. # engine and set the authProtocol and privProtocol if they
  110. # have been provided.
  111. foreach (keys %argv) {
  112. if (/^-?authoritative$/i) {
  113. $this->{_authoritative} = (delete $argv{$_}) ? TRUE : FALSE;
  114. } elsif (/^-?authprotocol$/i) {
  115. $this->_auth_protocol(delete $argv{$_});
  116. } elsif (/^-?privprotocol$/i) {
  117. $this->_priv_protocol(delete $argv{$_});
  118. }
  119. if (defined $this->{_error}) {
  120. return wantarray ? (undef, $this->{_error}) : undef;
  121. }
  122. }
  123. # Now validate the rest of the passed arguments
  124. for (keys %argv) {
  125. if (/^-?version$/i) {
  126. $this->_version($argv{$_});
  127. } elsif (/^-?debug$/i) {
  128. $this->debug($argv{$_});
  129. } elsif ((/^-?engineid$/i) && ($this->{_authoritative})) {
  130. $this->_engine_id($argv{$_});
  131. } elsif (/^-?username$/i) {
  132. $this->_user_name($argv{$_});
  133. } elsif (/^-?authkey$/i) {
  134. $this->_auth_key($argv{$_});
  135. } elsif (/^-?authpassword$/i) {
  136. $this->_auth_password($argv{$_});
  137. } elsif (/^-?privkey$/i) {
  138. $this->_priv_key($argv{$_});
  139. } elsif (/^-?privpassword$/i) {
  140. $this->_priv_password($argv{$_});
  141. } else {
  142. $this->_error('The argument "%s" is unknown', $_);
  143. }
  144. if (defined $this->{_error}) {
  145. return wantarray ? (undef, $this->{_error}) : undef;
  146. }
  147. }
  148. # Generate a snmpEngineID and populate the object accordingly
  149. # if we are an authoritative snmpEngine.
  150. if ($this->{_authoritative}) {
  151. $this->_snmp_engine_init();
  152. }
  153. # Define the securityParameters
  154. if (!defined $this->_security_params()) {
  155. return wantarray ? (undef, $this->{_error}) : undef;
  156. }
  157. # Return the object and an empty error message (in list context)
  158. return wantarray ? ($this, q{}) : $this;
  159. }
  160. sub generate_request_msg
  161. {
  162. my ($this, $pdu, $msg) = @_;
  163. # Clear any previous errors
  164. $this->_error_clear();
  165. if (@_ < 3) {
  166. return $this->_error('The required PDU and/or Message object is missing');
  167. }
  168. # Validate the SNMP version of the PDU
  169. if ($pdu->version() != $this->{_version}) {
  170. return $this->_error(
  171. 'The SNMP version %d was expected, but %d was found',
  172. $this->{_version}, $pdu->version()
  173. );
  174. }
  175. # Validate the securityLevel of the PDU
  176. if ($pdu->security_level() > $this->{_security_level}) {
  177. return $this->_error(
  178. 'The PDU securityLevel %d is greater than the configured value %d',
  179. $pdu->security_level(), $this->{_security_level}
  180. );
  181. }
  182. # Validate PDU type with snmpEngine type
  183. if ($pdu->expect_response()) {
  184. if ($this->{_authoritative}) {
  185. return $this->_error(
  186. 'Must be a non-authoritative SNMP engine to generate a %s',
  187. asn1_itoa($pdu->pdu_type())
  188. );
  189. }
  190. } else {
  191. if (!$this->{_authoritative}) {
  192. return $this->_error(
  193. 'Must be an authoritative SNMP engine to generate a %s',
  194. asn1_itoa($pdu->pdu_type())
  195. );
  196. }
  197. }
  198. # Extract the msgGlobalData out of the message
  199. my $msg_global_data = $msg->clear();
  200. # AES in the USM Section 3.1.2.1 - "The 128-bit IV is obtained as
  201. # the concatenation of the... ...snmpEngineBoots, ...snmpEngineTime,
  202. # and a local 64-bit integer. We store the current snmpEngineBoots
  203. # and snmpEngineTime before encrypting the PDU so that the computed
  204. # IV matches the transmitted msgAuthoritativeEngineBoots and
  205. # msgAuthoritativeEngineTime.
  206. my $msg_engine_time = $this->_engine_time();
  207. my $msg_engine_boots = $this->_engine_boots();
  208. # Copy the PDU into a "plain text" buffer
  209. my $pdu_buffer = $pdu->copy();
  210. my $priv_params = q{};
  211. # encryptedPDU::=OCTET STRING
  212. if ($pdu->security_level() > SECURITY_LEVEL_AUTHNOPRIV) {
  213. if (!defined $this->_encrypt_data($msg, $priv_params, $pdu_buffer)) {
  214. return $this->_error();
  215. }
  216. }
  217. # msgPrivacyParameters::=OCTET STRING
  218. if (!defined $msg->prepare(OCTET_STRING, $priv_params)) {
  219. return $this->_error($msg->error());
  220. }
  221. # msgAuthenticationParameters::=OCTET STRING
  222. my $auth_params = q{};
  223. my $auth_location = 0;
  224. if ($pdu->security_level() > SECURITY_LEVEL_NOAUTHNOPRIV) {
  225. # Save the location to fill in msgAuthenticationParameters later
  226. $auth_location = $msg->length() + $this->{_auth_maclen} + length $pdu_buffer;
  227. # Set the msgAuthenticationParameters to all zeros
  228. $auth_params = pack "x$this->{_auth_maclen}";
  229. }
  230. if (!defined $msg->prepare(OCTET_STRING, $auth_params)) {
  231. return $this->_error($msg->error());
  232. }
  233. # msgUserName::=OCTET STRING
  234. if (!defined $msg->prepare(OCTET_STRING, $pdu->security_name())) {
  235. return $this->_error($msg->error());
  236. }
  237. # msgAuthoritativeEngineTime::=INTEGER
  238. if (!defined $msg->prepare(INTEGER, $msg_engine_time)) {
  239. return $this->_error($msg->error());
  240. }
  241. # msgAuthoritativeEngineBoots::=INTEGER
  242. if (!defined $msg->prepare(INTEGER, $msg_engine_boots)) {
  243. return $this->_error($msg->error());
  244. }
  245. # msgAuthoritativeEngineID
  246. if (!defined $msg->prepare(OCTET_STRING, $this->_engine_id())) {
  247. return $this->_error($msg->error());
  248. }
  249. # UsmSecurityParameters::= SEQUENCE
  250. if (!defined $msg->prepare(SEQUENCE)) {
  251. return $this->_error($msg->error());
  252. }
  253. # msgSecurityParameters::=OCTET STRING
  254. if (!defined $msg->prepare(OCTET_STRING, $msg->clear())) {
  255. return $this->_error($msg->error());
  256. }
  257. # Append the PDU
  258. if (!defined $msg->append($pdu_buffer)) {
  259. return $this->_error($msg->error());
  260. }
  261. # Prepend the msgGlobalData
  262. if (!defined $msg->prepend($msg_global_data)) {
  263. return $this->_error($msg->error());
  264. }
  265. # version::=INTEGER
  266. if (!defined $msg->prepare(INTEGER, $this->{_version})) {
  267. return $this->_error($msg->error());
  268. }
  269. # message::=SEQUENCE
  270. if (!defined $msg->prepare(SEQUENCE)) {
  271. return $this->_error($msg->error());
  272. }
  273. # Apply authentication
  274. if ($pdu->security_level() > SECURITY_LEVEL_NOAUTHNOPRIV) {
  275. if (!defined $this->_authenticate_outgoing_msg($msg, $auth_location)) {
  276. return $this->_error($msg->error());
  277. }
  278. }
  279. # Return the Message
  280. return $msg;
  281. }
  282. sub process_incoming_msg
  283. {
  284. my ($this, $msg) = @_;
  285. # Clear any previous errors
  286. $this->_error_clear();
  287. return $this->_error('The required Message object is missing') if (@_ < 2);
  288. # msgSecurityParameters::=OCTET STRING
  289. my $msg_params = $msg->process(OCTET_STRING);
  290. return $this->_error($msg->error()) if !defined $msg_params;
  291. # Need to move the buffer index back to the begining of the data
  292. # portion of the OCTET STRING that contains the msgSecurityParameters.
  293. $msg->index($msg->index() - length $msg_params);
  294. # UsmSecurityParameters::=SEQUENCE
  295. return $this->_error($msg->error()) if !defined $msg->process(SEQUENCE);
  296. # msgAuthoritativeEngineID::=OCTET STRING
  297. my $msg_engine_id;
  298. if (!defined($msg_engine_id = $msg->process(OCTET_STRING))) {
  299. return $this->_error($msg->error());
  300. }
  301. # msgAuthoritativeEngineBoots::=INTEGER (0..2147483647)
  302. my $msg_engine_boots;
  303. if (!defined ($msg_engine_boots = $msg->process(INTEGER))) {
  304. return $this->_error($msg->error());
  305. }
  306. if (($msg_engine_boots < 0) || ($msg_engine_boots > 2147483647)) {
  307. return $this->_error(
  308. 'The msgAuthoritativeEngineBoots value %d is out of range ' .
  309. '(0..2147483647)', $msg_engine_boots
  310. );
  311. }
  312. # msgAuthoritativeEngineTime::=INTEGER (0..2147483647)
  313. my $msg_engine_time;
  314. if (!defined ($msg_engine_time = $msg->process(INTEGER))) {
  315. return $this->_error($msg->error());
  316. }
  317. if (($msg_engine_time < 0) || ($msg_engine_time > 2147483647)) {
  318. return $this->_error(
  319. 'The msgAuthoritativeEngineTime value %d is out of range ' .
  320. '(0..2147483647)', $msg_engine_time
  321. );
  322. }
  323. # msgUserName::=OCTET STRING (SIZE(0..32))
  324. if (!defined $msg->security_name($msg->process(OCTET_STRING))) {
  325. return $this->_error($msg->error());
  326. }
  327. # msgAuthenticationParameters::=OCTET STRING
  328. my $auth_params;
  329. if (!defined ($auth_params = $msg->process(OCTET_STRING))) {
  330. return $this->_error($msg->error());
  331. }
  332. # We need to zero out the msgAuthenticationParameters in order
  333. # to compute the HMAC properly.
  334. if (my $len = length $auth_params) {
  335. if ($len != $this->{_auth_maclen}) {
  336. return $this->_error(
  337. 'The msgAuthenticationParameters length of %d is invalid', $len
  338. );
  339. }
  340. substr ${$msg->reference}, ($msg->index() - $this->{_auth_maclen}), $this->{_auth_maclen}, pack "x$this->{_auth_maclen}";
  341. }
  342. # msgPrivacyParameters::=OCTET STRING
  343. my $priv_params;
  344. if (!defined ($priv_params = $msg->process(OCTET_STRING))) {
  345. return $this->_error($msg->error());
  346. }
  347. # Validate the msgAuthoritativeEngineID and msgUserName
  348. if ($this->{_discovered}) {
  349. if ($msg_engine_id ne $this->_engine_id()) {
  350. return $this->_error(
  351. 'The msgAuthoritativeEngineID "%s" was expected, but "%s" was ' .
  352. 'found', unpack('H*', $this->_engine_id()),
  353. unpack 'H*', $msg_engine_id
  354. );
  355. }
  356. if ($msg->security_name() ne $this->_user_name()) {
  357. return $this->_error(
  358. 'The msgUserName "%s" was expected, but "%s" was found',
  359. $this->_user_name(), $msg->security_name()
  360. );
  361. }
  362. } else {
  363. # Handle authoritativeEngineID discovery
  364. if (!defined $this->_engine_id_discovery($msg_engine_id)) {
  365. return $this->_error();
  366. }
  367. }
  368. # Validate the incoming securityLevel
  369. my $security_level = $msg->security_level();
  370. if ($security_level > $this->{_security_level}) {
  371. return $this->_error(
  372. 'The message securityLevel %d is greater than the configured ' .
  373. 'value %d', $security_level, $this->{_security_level}
  374. );
  375. }
  376. if ($security_level > SECURITY_LEVEL_NOAUTHNOPRIV) {
  377. # Authenticate the message
  378. if (!defined $this->_authenticate_incoming_msg($msg, $auth_params)) {
  379. return $this->_error();
  380. }
  381. # Synchronize the time
  382. if (!$this->_synchronize($msg_engine_boots, $msg_engine_time)) {
  383. return $this->_error();
  384. }
  385. # Check for timeliness
  386. if (!defined $this->_timeliness($msg_engine_boots, $msg_engine_time)) {
  387. return $this->_error();
  388. }
  389. if ($security_level > SECURITY_LEVEL_AUTHNOPRIV) {
  390. # Validate the msgPrivacyParameters length.
  391. if (length($priv_params) != 8) {
  392. return $this->_error(
  393. 'The msgPrivacyParameters length of %d is invalid',
  394. length $priv_params
  395. );
  396. }
  397. # AES in the USM Section 3.1.2.1 - "The 128-bit IV is
  398. # obtained as the concatenation of the... ...snmpEngineBoots,
  399. # ...snmpEngineTime, and a local 64-bit integer. ...The
  400. # 64-bit integer must be placed in the msgPrivacyParameters
  401. # field..." We must prepend the snmpEngineBoots and
  402. # snmpEngineTime as received in order to compute the IV.
  403. if (($this->{_priv_protocol} eq PRIV_PROTOCOL_AESCFB128) ||
  404. ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
  405. ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB256))
  406. {
  407. substr $priv_params, 0, 0, pack 'NN', $msg_engine_boots,
  408. $msg_engine_time;
  409. }
  410. # encryptedPDU::=OCTET STRING
  411. return $this->_decrypt_data($msg,
  412. $priv_params,
  413. $msg->process(OCTET_STRING));
  414. }
  415. }
  416. return TRUE;
  417. }
  418. sub user_name
  419. {
  420. return $_[0]->{_user_name};
  421. }
  422. sub auth_protocol
  423. {
  424. my ($this) = @_;
  425. if ($this->{_security_level} > SECURITY_LEVEL_NOAUTHNOPRIV) {
  426. return $this->{_auth_protocol};
  427. }
  428. return AUTH_PROTOCOL_NONE;
  429. }
  430. sub auth_key
  431. {
  432. return $_[0]->{_auth_key};
  433. }
  434. sub priv_protocol
  435. {
  436. my ($this) = @_;
  437. if ($this->{_security_level} > SECURITY_LEVEL_AUTHNOPRIV) {
  438. return $this->{_priv_protocol};
  439. }
  440. return PRIV_PROTOCOL_NONE;
  441. }
  442. sub priv_key
  443. {
  444. return $_[0]->{_priv_key};
  445. }
  446. sub engine_id
  447. {
  448. return $_[0]->{_engine_id};
  449. }
  450. sub engine_boots
  451. {
  452. goto _engine_boots;
  453. }
  454. sub engine_time
  455. {
  456. goto &_engine_time;
  457. }
  458. sub security_level
  459. {
  460. return $_[0]->{_security_level};
  461. }
  462. sub security_model
  463. {
  464. # RFC 3411 - SnmpSecurityModel::=TEXTUAL-CONVENTION
  465. return SECURITY_MODEL_USM;
  466. }
  467. sub security_name
  468. {
  469. goto &_user_name;
  470. }
  471. sub discovered
  472. {
  473. my ($this) = @_;
  474. if ($this->{_security_level} > SECURITY_LEVEL_NOAUTHNOPRIV) {
  475. return ($this->{_discovered} && $this->{_synchronized});
  476. }
  477. return $this->{_discovered};
  478. }
  479. # [private methods] ----------------------------------------------------------
  480. sub _version
  481. {
  482. my ($this, $version) = @_;
  483. if ($version != SNMP_VERSION_3) {
  484. return $this->_error('The SNMP version %s is not supported', $version);
  485. }
  486. return $this->{_version} = $version;
  487. }
  488. sub _engine_id
  489. {
  490. my ($this, $engine_id) = @_;
  491. if (@_ < 2) {
  492. return $this->{_engine_id};
  493. }
  494. if ($engine_id =~ m/^(?:0x)?([A-F0-9]+)$/i) {
  495. my $eid = pack 'H*', length($1) % 2 ? '0'.$1 : $1;
  496. my $len = length $eid;
  497. if ($len < 5 || $len > 32) {
  498. return $this->_error(
  499. 'The authoritativeEngineID length of %d is out of range (5..32)',
  500. $len
  501. );
  502. }
  503. $this->{_engine_id} = $eid;
  504. } else {
  505. return $this->_error(
  506. 'The authoritativeEngineID "%s" is expected in hexadecimal format',
  507. $engine_id
  508. );
  509. }
  510. return $this->{_engine_id};
  511. }
  512. sub _user_name
  513. {
  514. my ($this, $user_name) = @_;
  515. if (@_ == 2) {
  516. if ($user_name eq q{}) {
  517. return $this->_error('An empty userName was specified');
  518. } elsif (length($user_name) > 32) {
  519. return $this->_error(
  520. 'The userName length of %d is out of range (1..32)',
  521. length $user_name
  522. );
  523. }
  524. $this->{_user_name} = $user_name;
  525. }
  526. # RFC 3414 Section 4 - "Discovery... ...msgUserName of zero-length..."
  527. return ($this->{_discovered}) ? $this->{_user_name} : q{};
  528. }
  529. sub _snmp_engine_init
  530. {
  531. my ($this) = @_;
  532. if ($this->{_engine_id} eq q{}) {
  533. # Initialize our snmpEngineID using the algorithm described
  534. # in RFC 3411 - SnmpEngineID::=TEXTUAL-CONVENTION.
  535. # The first bit is set to one to indicate that the RFC 3411
  536. # algorithm is being used. The first fours bytes are to be
  537. # the agent's SNMP management private enterprise number, but
  538. # they are set to all zeros. The fifth byte is set to one to
  539. # indicate that the final four bytes are an IPv4 address.
  540. if (!defined $ENGINE_ID) {
  541. $ENGINE_ID = eval {
  542. require Sys::Hostname;
  543. pack('H10', '8000000001') . gethostbyname Sys::Hostname::hostname();
  544. };
  545. # Fallback in case gethostbyname() or hostname() fail
  546. if ($@) {
  547. $ENGINE_ID = pack 'x11H2', '01';
  548. }
  549. }
  550. $this->{_engine_id} = $ENGINE_ID;
  551. }
  552. $this->{_engine_boots} = 1;
  553. $this->{_time_epoc} = $^T;
  554. $this->{_synchronized} = TRUE;
  555. $this->{_discovered} = TRUE;
  556. return TRUE;
  557. }
  558. sub _auth_key
  559. {
  560. my ($this, $auth_key) = @_;
  561. if (@_ == 2) {
  562. if ($auth_key =~ m/^(?:0x)?([A-F0-9]+)$/i) {
  563. $this->{_auth_key} = pack 'H*', length($1) % 2 ? '0'.$1 : $1;
  564. if (!defined $this->_auth_key_validate()) {
  565. return $this->_error();
  566. }
  567. } else {
  568. return $this->_error(
  569. 'The authKey "%s" is expected in hexadecimal format', $auth_key
  570. );
  571. }
  572. }
  573. return $this->{_auth_key};
  574. }
  575. sub _auth_password
  576. {
  577. my ($this, $auth_password) = @_;
  578. if (@_ == 2) {
  579. if ($auth_password eq q{}) {
  580. return $this->_error('An empty authentication password was specified');
  581. }
  582. $this->{_auth_password} = $auth_password;
  583. }
  584. return $this->{_auth_password};
  585. }
  586. {
  587. my $protocols = {
  588. '(?:hmac-)?md5(?:-96)?', AUTH_PROTOCOL_HMACMD5,
  589. quotemeta AUTH_PROTOCOL_HMACMD5, AUTH_PROTOCOL_HMACMD5,
  590. '(?:hmac-)?sha(?:-?1|-96)?', AUTH_PROTOCOL_HMACSHA,
  591. quotemeta AUTH_PROTOCOL_HMACSHA, AUTH_PROTOCOL_HMACSHA,
  592. '(?:hmac-)?sha(?:-?224)', AUTH_PROTOCOL_HMACSHA224,
  593. 'usmHMAC128SHA224AuthProtocol', AUTH_PROTOCOL_HMACSHA224,
  594. quotemeta AUTH_PROTOCOL_HMACSHA224,AUTH_PROTOCOL_HMACSHA224,
  595. '(?:hmac-)?sha(?:-?256)', AUTH_PROTOCOL_HMACSHA256,
  596. 'usmHMAC192SHA256AuthProtocol', AUTH_PROTOCOL_HMACSHA256,
  597. quotemeta AUTH_PROTOCOL_HMACSHA256,AUTH_PROTOCOL_HMACSHA256,
  598. '(?:hmac-)?sha(?:-?384)', AUTH_PROTOCOL_HMACSHA384,
  599. 'usmHMAC256SHA384AuthProtocol', AUTH_PROTOCOL_HMACSHA384,
  600. quotemeta AUTH_PROTOCOL_HMACSHA384,AUTH_PROTOCOL_HMACSHA384,
  601. '(?:hmac-)?sha(?:-?512)', AUTH_PROTOCOL_HMACSHA512,
  602. 'usmHMAC384SHA512AuthProtocol', AUTH_PROTOCOL_HMACSHA512,
  603. quotemeta AUTH_PROTOCOL_HMACSHA512,AUTH_PROTOCOL_HMACSHA512,
  604. };
  605. sub _auth_protocol
  606. {
  607. my ($this, $proto) = @_;
  608. if (@_ < 2) {
  609. return $this->{_auth_protocol};
  610. }
  611. if ($proto eq q{}) {
  612. return $this->_error('An empty authProtocol was specified');
  613. }
  614. for (keys %{$protocols}) {
  615. if ($proto =~ /^$_$/i) {
  616. return $this->{_auth_protocol} = $protocols->{$_};
  617. }
  618. }
  619. return $this->_error('The authProtocol "%s" is unknown', $proto);
  620. }
  621. }
  622. sub _priv_key
  623. {
  624. my ($this, $priv_key) = @_;
  625. if (@_ == 2) {
  626. if ($priv_key =~ m/^(?:0x)?([A-F0-9]+)$/i) {
  627. $this->{_priv_key} = pack 'H*', length($1) % 2 ? '0'.$1 : $1;
  628. if (!defined $this->_priv_key_validate()) {
  629. return $this->_error();
  630. }
  631. } else {
  632. return $this->_error(
  633. 'The privKey "%s" is expected in hexadecimal format', $priv_key
  634. );
  635. }
  636. }
  637. return $this->{_priv_key};
  638. }
  639. sub _priv_password
  640. {
  641. my ($this, $priv_password) = @_;
  642. if (@_ == 2) {
  643. if ($priv_password eq q{}) {
  644. return $this->_error('An empty privacy password was specified');
  645. }
  646. $this->{_priv_password} = $priv_password;
  647. }
  648. return $this->{_priv_password};
  649. }
  650. {
  651. my $protocols = {
  652. '(?:cbc-)?des', PRIV_PROTOCOL_DES,
  653. quotemeta PRIV_PROTOCOL_DES, PRIV_PROTOCOL_DES,
  654. '(?:cbc-)?(?:3|triple-)des(?:-?ede)?', PRIV_PROTOCOL_DRAFT_3DESEDE,
  655. quotemeta PRIV_PROTOCOL_DRAFT_3DESEDE, PRIV_PROTOCOL_DRAFT_3DESEDE,
  656. '(?:(?:cfb)?128-?)?aes(?:-?128)?', PRIV_PROTOCOL_AESCFB128,
  657. quotemeta PRIV_PROTOCOL_AESCFB128, PRIV_PROTOCOL_AESCFB128,
  658. quotemeta PRIV_PROTOCOL_DRAFT_AESCFB128, PRIV_PROTOCOL_AESCFB128,
  659. '(?:(?:cfb)?192-?)aes(?:-?128)?', PRIV_PROTOCOL_DRAFT_AESCFB192,
  660. quotemeta PRIV_PROTOCOL_DRAFT_AESCFB192, PRIV_PROTOCOL_DRAFT_AESCFB192,
  661. '(?:(?:cfb)?256-?)aes(?:-?128)?', PRIV_PROTOCOL_DRAFT_AESCFB256,
  662. quotemeta PRIV_PROTOCOL_DRAFT_AESCFB256, PRIV_PROTOCOL_DRAFT_AESCFB256,
  663. };
  664. sub _priv_protocol
  665. {
  666. my ($this, $proto) = @_;
  667. if (@_ < 2) {
  668. return $this->{_priv_protocol};
  669. }
  670. if ($proto eq q{}) {
  671. return $this->_error('An empty privProtocol was specified');
  672. }
  673. my $priv_proto;
  674. for (keys %{$protocols}) {
  675. if ($proto =~ /^$_$/i) {
  676. $priv_proto = $protocols->{$_};
  677. last;
  678. }
  679. }
  680. if (!defined $priv_proto) {
  681. return $this->_error('The privProtocol "%s" is unknown', $proto);
  682. }
  683. # Validate the support of the AES cipher algorithm. Attempt to
  684. # load the Crypt::Rijndael module. If this module is not found,
  685. # do not provide support for the AES Cipher Algorithm.
  686. if (($priv_proto eq PRIV_PROTOCOL_AESCFB128) ||
  687. ($priv_proto eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
  688. ($priv_proto eq PRIV_PROTOCOL_DRAFT_AESCFB256))
  689. {
  690. if (defined (my $error = load_module('Crypt::Rijndael'))) {
  691. return $this->_error(
  692. 'Support for privProtocol "%s" is unavailable %s', $proto, $error
  693. );
  694. }
  695. }
  696. return $this->{_priv_protocol} = $priv_proto;
  697. }
  698. }
  699. sub _engine_boots
  700. {
  701. return ($_[0]->{_synchronized}) ? $_[0]->{_engine_boots} : 0;
  702. }
  703. sub _engine_time
  704. {
  705. my ($this) = @_;
  706. return 0 if (!$this->{_synchronized});
  707. $this->{_engine_time} = time() - $this->{_time_epoc};
  708. if ($this->{_engine_time} > 2147483647) {
  709. DEBUG_INFO('snmpEngineTime rollover');
  710. if (++$this->{_engine_boots} == 2147483647) {
  711. die 'FATAL: Unable to handle snmpEngineBoots value';
  712. }
  713. $this->{_engine_time} -= 2147483647;
  714. $this->{_time_epoc} = time() - $this->{_engine_time};
  715. if (!$this->{_authoritative}) {
  716. $this->{_synchronized} = FALSE;
  717. return $this->{_latest_engine_time} = 0;
  718. }
  719. }
  720. if ($this->{_engine_time} < 0) {
  721. die 'FATAL: Unable to handle negative snmpEngineTime value';
  722. }
  723. return $this->{_engine_time};
  724. }
  725. sub _security_params
  726. {
  727. my ($this) = @_;
  728. # Clear any previous error messages
  729. $this->_error_clear();
  730. # We must have an usmUserName
  731. if ($this->{_user_name} eq q{}) {
  732. return $this->_error('The required userName was not specified');
  733. }
  734. # Define the authentication parameters
  735. if ((defined $this->{_auth_password}) && ($this->{_discovered})) {
  736. if (!defined $this->{_auth_key}) {
  737. return $this->_error() if !defined $this->_auth_key_generate();
  738. }
  739. $this->{_auth_password} = undef;
  740. }
  741. if (defined $this->{_auth_key}) {
  742. # Validate the key based on the protocol
  743. if (!defined $this->_auth_key_validate()) {
  744. return $this->_error('The authKey is invalid');
  745. }
  746. # Initialize the authentication data
  747. if (!defined $this->_auth_data_init()) {
  748. return $this->_error('Failed to initialize the authentication data');
  749. }
  750. if ($this->{_discovered}) {
  751. $this->{_security_level} = SECURITY_LEVEL_AUTHNOPRIV;
  752. }
  753. }
  754. # You must have authentication to have privacy
  755. if (!defined ($this->{_auth_key}) && !defined $this->{_auth_password}) {
  756. if (defined ($this->{_priv_key}) || defined $this->{_priv_password}) {
  757. return $this->_error(
  758. 'The securityLevel is unsupported (privacy requires authentication)'
  759. );
  760. }
  761. }
  762. # Define the privacy parameters
  763. if ((defined $this->{_priv_password}) && ($this->{_discovered})) {
  764. if (!defined $this->{_priv_key}) {
  765. return $this->_error() if !defined $this->_priv_key_generate();
  766. }
  767. $this->{_priv_password} = undef;
  768. }
  769. if (defined $this->{_priv_key}) {
  770. # Validate the key based on the protocol
  771. if (!defined $this->_priv_key_validate()) {
  772. return $this->_error('The privKey is invalid');
  773. }
  774. # Initialize the privacy data
  775. if (!defined $this->_priv_data_init()) {
  776. return $this->_error('Failed to initialize the privacy data');
  777. }
  778. if ($this->{_discovered}) {
  779. $this->{_security_level} = SECURITY_LEVEL_AUTHPRIV;
  780. }
  781. }
  782. DEBUG_INFO('securityLevel = %d', $this->{_security_level});
  783. return $this->{_security_level};
  784. }
  785. sub _engine_id_discovery
  786. {
  787. my ($this, $engine_id) = @_;
  788. return TRUE if ($this->{_authoritative});
  789. DEBUG_INFO('engineID = 0x%s', unpack 'H*', $engine_id || q{});
  790. if (length($engine_id) < 5 || length($engine_id) > 32) {
  791. return $this->_error(
  792. 'The msgAuthoritativeEngineID length of %d is out of range (5..32)',
  793. length $engine_id
  794. );
  795. }
  796. $this->{_engine_id} = $engine_id;
  797. $this->{_discovered} = TRUE;
  798. if (!defined $this->_security_params()) {
  799. $this->{_discovered} = FALSE;
  800. return $this->_error();
  801. }
  802. return TRUE;
  803. }
  804. sub _synchronize
  805. {
  806. my ($this, $msg_boots, $msg_time) = @_;
  807. return TRUE if ($this->{_authoritative});
  808. return TRUE if ($this->{_security_level} < SECURITY_LEVEL_AUTHNOPRIV);
  809. if (($msg_boots > $this->_engine_boots()) ||
  810. (($msg_boots == $this->_engine_boots()) &&
  811. ($msg_time > $this->{_latest_engine_time})))
  812. {
  813. DEBUG_INFO(
  814. 'update: engineBoots = %d, engineTime = %d', $msg_boots, $msg_time
  815. );
  816. $this->{_engine_boots} = $msg_boots;
  817. $this->{_latest_engine_time} = $this->{_engine_time} = $msg_time;
  818. $this->{_time_epoc} = time() - $this->{_engine_time};
  819. if (!$this->{_synchronized}) {
  820. $this->{_synchronized} = TRUE;
  821. if (!defined $this->_security_params()) {
  822. return ($this->{_synchronized} = FALSE);
  823. }
  824. }
  825. return TRUE;
  826. }
  827. DEBUG_INFO(
  828. 'no update: engineBoots = %d, msgBoots = %d; ' .
  829. 'latestTime = %d, msgTime = %d',
  830. $this->_engine_boots(), $msg_boots,
  831. $this->{_latest_engine_time}, $msg_time
  832. );
  833. return TRUE;
  834. }
  835. sub _timeliness
  836. {
  837. my ($this, $msg_boots, $msg_time) = @_;
  838. return TRUE if ($this->{_security_level} < SECURITY_LEVEL_AUTHNOPRIV);
  839. # Retrieve a local copy of our snmpEngineBoots and snmpEngineTime
  840. # to avoid the possibilty of using different values in each of
  841. # the comparisons.
  842. my $engine_time = $this->_engine_time();
  843. my $engine_boots = $this->_engine_boots();
  844. if ($engine_boots == 2147483647) {
  845. $this->{_synchronized} = FALSE;
  846. return $this->_error('The system is not in the time window');
  847. }
  848. if (!$this->{_authoritative}) {
  849. if ($msg_boots < $engine_boots) {
  850. return $this->_error('The message is not in the time window');
  851. }
  852. if (($msg_boots == $engine_boots) && ($msg_time < ($engine_time - 150))) {
  853. return $this->_error('The message is not in the time window');
  854. }
  855. } else {
  856. if ($msg_boots != $engine_boots) {
  857. return $this->_error('The message is not in the time window');
  858. }
  859. if (($msg_time < ($engine_time - 150)) ||
  860. ($msg_time > ($engine_time + 150)))
  861. {
  862. return $this->_error('The message is not in the time window');
  863. }
  864. }
  865. return TRUE;
  866. }
  867. sub _authenticate_outgoing_msg
  868. {
  869. my ($this, $msg, $auth_location) = @_;
  870. if (!$auth_location) {
  871. return $this->_error(
  872. 'Authentication failure (Unable to set msgAuthenticationParameters)'
  873. );
  874. }
  875. # Set the msgAuthenticationParameters
  876. substr ${$msg->reference}, -$auth_location, $this->{_auth_maclen}, $this->_auth_hmac($msg);
  877. return TRUE;
  878. }
  879. sub _authenticate_incoming_msg
  880. {
  881. my ($this, $msg, $auth_params) = @_;
  882. # Authenticate the message
  883. if ($auth_params ne $this->_auth_hmac($msg)) {
  884. return $this->_error('Authentication failure');
  885. }
  886. DEBUG_INFO('authentication passed');
  887. return TRUE;
  888. }
  889. sub _auth_hmac
  890. {
  891. my ($this, $msg) = @_;
  892. return q{} if (!defined($this->{_auth_data}) || !defined $msg);
  893. return substr
  894. $this->{_auth_data}(${$msg->reference()}, $this->{_auth_key}), 0, $this->{_auth_maclen};
  895. }
  896. sub _auth_data_init
  897. {
  898. my ($this) = @_;
  899. if (!defined $this->{_auth_key}) {
  900. return $this->_error('The required authKey is not defined');
  901. }
  902. return TRUE if defined $this->{_auth_data};
  903. if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
  904. $this->{_auth_data} = \&hmac_md5;
  905. $this->{_auth_maclen} = 12;
  906. } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
  907. $this->{_auth_data} = \&hmac_sha1;
  908. $this->{_auth_maclen} = 12;
  909. } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA224) {
  910. $this->{_auth_data} = \&hmac_sha224;
  911. $this->{_auth_maclen} = 16;
  912. } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA256) {
  913. $this->{_auth_data} = \&hmac_sha256;
  914. $this->{_auth_maclen} = 24;
  915. } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA384) {
  916. $this->{_auth_data} = \&hmac_sha384;
  917. $this->{_auth_maclen} = 32;
  918. } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA512) {
  919. $this->{_auth_data} = \&hmac_sha512;
  920. $this->{_auth_maclen} = 48;
  921. } else {
  922. return $this->_error(
  923. 'The authProtocol "%s" is unknown', $this->{_auth_protocol}
  924. );
  925. }
  926. return TRUE;
  927. }
  928. {
  929. my $encrypt =
  930. {
  931. PRIV_PROTOCOL_DES, \&_priv_encrypt_des,
  932. PRIV_PROTOCOL_DRAFT_3DESEDE, \&_priv_encrypt_3desede,
  933. PRIV_PROTOCOL_AESCFB128, \&_priv_encrypt_aescfbxxx,
  934. PRIV_PROTOCOL_DRAFT_AESCFB192, \&_priv_encrypt_aescfbxxx,
  935. PRIV_PROTOCOL_DRAFT_AESCFB256, \&_priv_encrypt_aescfbxxx
  936. };
  937. sub _encrypt_data
  938. {
  939. # my ($this, $msg, $priv_params, $plain) = @_;
  940. if (!exists $encrypt->{$_[0]->{_priv_protocol}}) {
  941. return $_[0]->_error('Encryption error (Unknown protocol)');
  942. }
  943. if (!defined
  944. $_[1]->prepare(
  945. OCTET_STRING,
  946. $_[0]->${\$encrypt->{$_[0]->{_priv_protocol}}}($_[2], $_[3])
  947. )
  948. )
  949. {
  950. return $_[0]->_error('Encryption error');
  951. }
  952. # Set the PDU buffer equal to the encryptedPDU
  953. return $_[3] = $_[1]->clear();
  954. }
  955. }
  956. {
  957. my $decrypt =
  958. {
  959. PRIV_PROTOCOL_DES, \&_priv_decrypt_des,
  960. PRIV_PROTOCOL_DRAFT_3DESEDE, \&_priv_decrypt_3desede,
  961. PRIV_PROTOCOL_AESCFB128, \&_priv_decrypt_aescfbxxx,
  962. PRIV_PROTOCOL_DRAFT_AESCFB192, \&_priv_decrypt_aescfbxxx,
  963. PRIV_PROTOCOL_DRAFT_AESCFB256, \&_priv_decrypt_aescfbxxx
  964. };
  965. sub _decrypt_data
  966. {
  967. # my ($this, $msg, $priv_params, $cipher) = @_;
  968. # Make sure there is data to decrypt.
  969. if (!defined $_[3]) {
  970. return $_[0]->_error($_[1]->error() || 'Decryption error (No data)');
  971. }
  972. if (!exists $decrypt->{$_[0]->{_priv_protocol}}) {
  973. return $_[0]->_error('Decryption error (Unknown protocol)');
  974. }
  975. # Clear the Message buffer
  976. $_[1]->clear();
  977. # Put the decrypted data back into the Message buffer
  978. if (!defined
  979. $_[1]->prepend(
  980. $_[0]->${\$decrypt->{$_[0]->{_priv_protocol}}}($_[2], $_[3])
  981. )
  982. )
  983. {
  984. return $_[0]->_error($_[1]->error());
  985. }
  986. return $_[0]->_error($_[1]->error()) if (!$_[1]->length());
  987. # See if the decrypted data starts with a SEQUENCE
  988. # and has a reasonable length.
  989. my $msglen = $_[1]->process(SEQUENCE);
  990. if ((!defined $msglen) || ($msglen > $_[1]->length())) {
  991. return $_[0]->_error('Decryption error');
  992. }
  993. $_[1]->index(0); # Reset the index
  994. DEBUG_INFO('privacy passed');
  995. return TRUE;
  996. }
  997. }
  998. sub _priv_data_init
  999. {
  1000. my ($this) = @_;
  1001. if (!defined $this->{_priv_key}) {
  1002. return $this->_error('The required privKey is not defined');
  1003. }
  1004. return TRUE if defined $this->{_priv_data};
  1005. my $init =
  1006. {
  1007. PRIV_PROTOCOL_DES, \&_priv_data_init_des,
  1008. PRIV_PROTOCOL_DRAFT_3DESEDE, \&_priv_data_init_3desede,
  1009. PRIV_PROTOCOL_AESCFB128, \&_priv_data_init_aescfbxxx,
  1010. PRIV_PROTOCOL_DRAFT_AESCFB192, \&_priv_data_init_aescfbxxx,
  1011. PRIV_PROTOCOL_DRAFT_AESCFB256, \&_priv_data_init_aescfbxxx
  1012. };
  1013. if (!exists $init->{$this->{_priv_protocol}}) {
  1014. return $this->_error(
  1015. 'The privProtocol "%s" is unknown', $this->{_priv_protocol}
  1016. );
  1017. }
  1018. return $this->${\$init->{$this->{_priv_protocol}}}();
  1019. }
  1020. sub _priv_data_init_des
  1021. {
  1022. my ($this) = @_;
  1023. if (!defined $this->{_priv_key}) {
  1024. return $this->_error('The required privKey is not defined');
  1025. }
  1026. # Create the DES object
  1027. $this->{_priv_data}->{des} =
  1028. Crypt::DES->new(substr $this->{_priv_key}, 0, 8);
  1029. # Extract the pre-IV
  1030. $this->{_priv_data}->{pre_iv} = substr $this->{_priv_key}, 8, 8;
  1031. # Initialize the salt
  1032. $this->{_priv_data}->{salt} = int rand ~0;
  1033. return TRUE;
  1034. }
  1035. sub _priv_encrypt_des
  1036. {
  1037. # my ($this, $priv_params, $plain) = @_;
  1038. if (!defined $_[0]->{_priv_data}) {
  1039. return $_[0]->_error('The required privacy data is not defined');
  1040. }
  1041. # Always pad the plain text data. "The actual pad value is
  1042. # irrelevant..." according RFC 3414 Section 8.1.1.2. However,
  1043. # there are some agents out there that expect "standard block
  1044. # padding" where each of the padding byte(s) are set to the size
  1045. # of the padding (even for data that is a multiple of block size).
  1046. my $pad = 8 - (length($_[2]) % 8);
  1047. $_[2] .= pack('C', $pad) x $pad;
  1048. # Create and set the salt
  1049. if ($_[0]->{_priv_data}->{salt}++ == ~0) {
  1050. $_[0]->{_priv_data}->{salt} = 0;
  1051. }
  1052. $_[1] = pack 'NN', $_[0]->{_engine_boots}, $_[0]->{_priv_data}->{salt};
  1053. # Create the initial vector (IV)
  1054. my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
  1055. my $cipher = q{};
  1056. # Perform Cipher Block Chaining (CBC)
  1057. while ($_[2] =~ /(.{8})/gs) {
  1058. $cipher .= $iv = $_[0]->{_priv_data}->{des}->encrypt($1 ^ $iv);
  1059. }
  1060. return $cipher;
  1061. }
  1062. sub _priv_decrypt_des
  1063. {
  1064. # my ($this, $priv_params, $cipher) = @_;
  1065. if (!defined $_[0]->{_priv_data}) {
  1066. return $_[0]->_error('The required privacy data is not defined');
  1067. }
  1068. if (length($_[1]) != 8) {
  1069. return $_[0]->_error(
  1070. 'The msgPrivParameters length of %d is invalid', length $_[1]
  1071. );
  1072. }
  1073. if (length($_[2]) % 8) {
  1074. return $_[0]->_error(
  1075. 'The DES cipher length is not a multiple of the block size'
  1076. );
  1077. }
  1078. # Create the initial vector (IV)
  1079. my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
  1080. my $plain = q{};
  1081. # Perform Cipher Block Chaining (CBC)
  1082. while ($_[2] =~ /(.{8})/gs) {
  1083. $plain .= $iv ^ $_[0]->{_priv_data}->{des}->decrypt($1);
  1084. $iv = $1;
  1085. }
  1086. return $plain;
  1087. }
  1088. sub _priv_data_init_3desede
  1089. {
  1090. my ($this) = @_;
  1091. if (!defined $this->{_priv_key}) {
  1092. return $this->_error('The required privKey is not defined');
  1093. }
  1094. # Create the 3 DES objects
  1095. $this->{_priv_data}->{des1} =
  1096. Crypt::DES->new(substr $this->{_priv_key}, 0, 8);
  1097. $this->{_priv_data}->{des2} =
  1098. Crypt::DES->new(substr $this->{_priv_key}, 8, 8);
  1099. $this->{_priv_data}->{des3} =
  1100. Crypt::DES->new(substr $this->{_priv_key}, 16, 8);
  1101. # Extract the pre-IV
  1102. $this->{_priv_data}->{pre_iv} = substr $this->{_priv_key}, 24, 8;
  1103. # Initialize the salt
  1104. $this->{_priv_data}->{salt} = int rand ~0;
  1105. # Assign a hash algorithm to "bit spread" the salt
  1106. if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
  1107. $this->{_priv_data}->{hash} = Digest::MD5->new();
  1108. } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
  1109. $this->{_priv_data}->{hash} = Digest::SHA->new();
  1110. }
  1111. return TRUE;
  1112. }
  1113. sub _priv_encrypt_3desede
  1114. {
  1115. # my ($this, $priv_params, $plain) = @_;
  1116. if (!defined $_[0]->{_priv_data}) {
  1117. return $_[0]->_error('The required privacy data is not defined');
  1118. }
  1119. # Pad the plain text data using "standard block padding".
  1120. my $pad = 8 - (length($_[2]) % 8);
  1121. $_[2] .= pack('C', $pad) x $pad;
  1122. # Create and set the salt
  1123. if ($_[0]->{_priv_data}->{salt}++ == ~0) {
  1124. $_[0]->{_priv_data}->{salt} = 0;
  1125. }
  1126. $_[1] = pack 'NN', $_[0]->{_engine_boots}, $_[0]->{_priv_data}->{salt};
  1127. # Draft 3DES-EDE for USM Section 5.1.1.1.2 - "To achieve effective
  1128. # bit spreading, the complete 8-octet 'salt' value SHOULD be
  1129. # hashed using the usmUserAuthProtocol."
  1130. if (exists $_[0]->{_priv_data}->{hash}) {
  1131. $_[1] = substr $_[0]->{_priv_data}->{hash}->add($_[1])->digest(), 0, 8;
  1132. }
  1133. # Create the initial vector (IV)
  1134. my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
  1135. my $cipher = q{};
  1136. # Perform Cipher Block Chaining (CBC)
  1137. while ($_[2] =~ /(.{8})/gs) {
  1138. $cipher .= $iv =
  1139. $_[0]->{_priv_data}->{des3}->encrypt(
  1140. $_[0]->{_priv_data}->{des2}->decrypt(
  1141. $_[0]->{_priv_data}->{des1}->encrypt($1 ^ $iv)
  1142. )
  1143. );
  1144. }
  1145. return $cipher;
  1146. }
  1147. sub _priv_decrypt_3desede
  1148. {
  1149. # my ($this, $priv_params, $cipher) = @_;
  1150. if (!defined $_[0]->{_priv_data}) {
  1151. return $_[0]->_error('The required privacy data is not defined');
  1152. }
  1153. if (length($_[1]) != 8) {
  1154. return $_[0]->_error(
  1155. 'The msgPrivParameters length of %d is invalid', length $_[1]
  1156. );
  1157. }
  1158. if (length($_[2]) % 8) {
  1159. return $_[0]->_error(
  1160. 'The CBC-3DES-EDE cipher length is not a multiple of the block size'
  1161. );
  1162. }
  1163. # Create the initial vector (IV)
  1164. my $iv = $_[0]->{_priv_data}->{pre_iv} ^ $_[1];
  1165. my $plain = q{};
  1166. # Perform Cipher Block Chaining (CBC)
  1167. while ($_[2] =~ /(.{8})/gs) {
  1168. $plain .=
  1169. $iv ^ $_[0]->{_priv_data}->{des1}->decrypt(
  1170. $_[0]->{_priv_data}->{des2}->encrypt(
  1171. $_[0]->{_priv_data}->{des3}->decrypt($1)
  1172. )
  1173. );
  1174. $iv = $1;
  1175. }
  1176. return $plain;
  1177. }
  1178. sub _priv_data_init_aescfbxxx
  1179. {
  1180. my ($this) = @_;
  1181. if (!defined $this->{_priv_key}) {
  1182. return $this->_error('The required privKey is not defined');
  1183. }
  1184. {
  1185. # Avoid a "strict subs" error if Crypt::Rijndael is not loaded.
  1186. no strict 'subs';
  1187. # Create the AES (Rijndael) object with a 128, 192, or 256 bit key.
  1188. $this->{_priv_data}->{aes} =
  1189. Crypt::Rijndael->new($this->{_priv_key}, Crypt::Rijndael::MODE_CFB());
  1190. }
  1191. # Initialize the salt
  1192. $this->{_priv_data}->{salt1} = int rand ~0;
  1193. $this->{_priv_data}->{salt2} = int rand ~0;
  1194. return TRUE;
  1195. }
  1196. sub _priv_encrypt_aescfbxxx
  1197. {
  1198. # my ($this, $priv_params, $plain) = @_;
  1199. if (!defined $_[0]->{_priv_data}) {
  1200. return $_[0]->_error('The required privacy data is not defined');
  1201. }
  1202. # Validate the plain text length
  1203. my $length = length $_[2];
  1204. if ($length <= 16) {
  1205. return $_[0]->_error(
  1206. 'The AES plain text length is not greater than the block size'
  1207. );
  1208. }
  1209. # Create and set the salt
  1210. if ($_[0]->{_priv_data}->{salt1}++ == ~0) {
  1211. $_[0]->{_priv_data}->{salt1} = 0;
  1212. if ($_[0]->{_priv_data}->{salt2}++ == ~0) {
  1213. $_[0]->{_priv_data}->{salt2} = 0;
  1214. }
  1215. }
  1216. $_[1] = pack 'NN', $_[0]->{_priv_data}->{salt2},
  1217. $_[0]->{_priv_data}->{salt1};
  1218. # AES in the USM Section - Section 3.1.3 "The last ciphertext
  1219. # block is produced by exclusive-ORing the last plaintext segment
  1220. # of r bits (r is less or equal to 128) with the segment of the r
  1221. # most significant bits of the last output block."
  1222. # This operation is identical to those performed on the previous
  1223. # blocks except for the fact that the block can be less than the
  1224. # block size. We can just pad the last block and operate on it as
  1225. # usual and then ignore the padding after encrypting.
  1226. $_[2] .= "\000" x (16 - ($length % 16));
  1227. # Create the IV by concatenating "...the generating SNMP engine's
  1228. # 32-bit snmpEngineBoots, the SNMP engine's 32-bit snmpEngineTime,
  1229. # and a local 64-bit integer..."
  1230. $_[0]->{_priv_data}->{aes}->set_iv(
  1231. pack('NN', $_[0]->{_engine_boots}, $_[0]->{_engine_time}) . $_[1]
  1232. );
  1233. # Let the Crypt::Rijndael module perform 128 bit Cipher Feedback
  1234. # (CFB) and return the result minus the "internal" padding.
  1235. return substr $_[0]->{_priv_data}->{aes}->encrypt($_[2]), 0, $length;
  1236. }
  1237. sub _priv_decrypt_aescfbxxx
  1238. {
  1239. # my ($this, $priv_params, $cipher) = @_;
  1240. if (!defined $_[0]->{_priv_data}) {
  1241. return $_[0]->_error('The required privacy data is not defined');
  1242. }
  1243. # Validate the msgPrivParameters length. We assume that the
  1244. # msgAuthoritativeEngineBoots and msgAuthoritativeEngineTime
  1245. # have been prepended to the msgPrivParameters to create the
  1246. # required 128 bit IV.
  1247. if (length($_[1]) != 16) {
  1248. return $_[0]->_error(
  1249. 'The AES IV length of %d is invalid', length $_[1]
  1250. );
  1251. }
  1252. # Validate the cipher length
  1253. my $length = length $_[2];
  1254. if ($length <= 16) {
  1255. return $_[0]->_error(
  1256. 'The AES cipher length is not greater than the block size'
  1257. );
  1258. }
  1259. # AES in the USM Section - Section 3.1.4 "The last ciphertext
  1260. # block (whose size r is less or equal to 128) is less or equal
  1261. # to 128) is exclusive-ORed with the segment of the r most
  1262. # significant bits of the last output block to recover the last
  1263. # plaintext block of r bits."
  1264. # This operation is identical to those performed on the previous
  1265. # blocks except for the fact that the block can be less than the
  1266. # block size. We can just pad the last block and operate on it as
  1267. # usual and then ignore the padding after decrypting.
  1268. $_[2] .= "\000" x (16 - ($length % 16));
  1269. # Use the msgPrivParameters as the IV.
  1270. $_[0]->{_priv_data}->{aes}->set_iv($_[1]);
  1271. # Let the Crypt::Rijndael module perform 128 bit Cipher Feedback
  1272. # (CFB) and return the result minus the "internal" padding.
  1273. return substr $_[0]->{_priv_data}->{aes}->decrypt($_[2]), 0, $length;
  1274. }
  1275. sub _auth_key_generate
  1276. {
  1277. my ($this) = @_;
  1278. if (!defined($this->{_engine_id}) || !defined $this->{_auth_password}) {
  1279. return $this->_error('Unable to generate the authKey');
  1280. }
  1281. $this->{_auth_key} = $this->_password_localize($this->{_auth_password});
  1282. return $this->{_auth_key};
  1283. }
  1284. sub _auth_key_validate
  1285. {
  1286. my ($this) = @_;
  1287. my $key_len =
  1288. {
  1289. AUTH_PROTOCOL_HMACMD5, [ 16, 'HMAC-MD5' ],
  1290. AUTH_PROTOCOL_HMACSHA, [ 20, 'HMAC-SHA' ],
  1291. AUTH_PROTOCOL_HMACSHA224, [ 28, 'HMAC-SHA224' ],
  1292. AUTH_PROTOCOL_HMACSHA256, [ 32, 'HMAC-SHA256' ],
  1293. AUTH_PROTOCOL_HMACSHA384, [ 48, 'HMAC-SHA384' ],
  1294. AUTH_PROTOCOL_HMACSHA512, [ 64, 'HMAC-SHA512' ],
  1295. };
  1296. if (!exists $key_len->{$this->{_auth_protocol}}) {
  1297. return $this->_error(
  1298. 'The authProtocol "%s" is unknown', $this->{_auth_protocol}
  1299. );
  1300. }
  1301. if (length($this->{_auth_key}) != $key_len->{$this->{_auth_protocol}}->[0])
  1302. {
  1303. return $this->_error(
  1304. 'The %s authKey length of %d is invalid, expected %d',
  1305. $key_len->{$this->{_auth_protocol}}->[1], length($this->{_auth_key}),
  1306. $key_len->{$this->{_auth_protocol}}->[0]
  1307. );
  1308. }
  1309. return TRUE;
  1310. }
  1311. sub _priv_key_generate
  1312. {
  1313. my ($this) = @_;
  1314. if (!defined($this->{_engine_id}) || !defined $this->{_priv_password}) {
  1315. return $this->_error('Unable to generate the privKey');
  1316. }
  1317. $this->{_priv_key} = $this->_password_localize($this->{_priv_password});
  1318. return $this->_error() if !defined $this->{_priv_key};
  1319. if ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_3DESEDE) {
  1320. # Draft 3DES-EDE for USM Section 2.1 - "To acquire the necessary
  1321. # number of key bits, the password-to-key algorithm may be chained
  1322. # using its output as further input in order to generate an
  1323. # appropriate number of key bits."
  1324. $this->{_priv_key} .= $this->_password_localize($this->{_priv_key});
  1325. } elsif (($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB192) ||
  1326. ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_AESCFB256))
  1327. {
  1328. # Draft AES in the USM Section 3.1.2.1 - "...if the size of the
  1329. # localized key is not large enough to generate an encryption
  1330. # key... ...set Kul = Kul || Hnnn(Kul) where Hnnn is the hash
  1331. # function for the authentication protocol..."
  1332. my $hnnn;
  1333. if ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACMD5) {
  1334. $hnnn = Digest::MD5->new();
  1335. } elsif ($this->{_auth_protocol} eq AUTH_PROTOCOL_HMACSHA) {
  1336. $hnnn = Digest::SHA->new();
  1337. } else {
  1338. return $this->_error(
  1339. 'The authProtocol "%s" is unknown', $this->{_auth_protocol}
  1340. );
  1341. }
  1342. $this->{_priv_key} .= $hnnn->add($this->{_priv_key})->digest();
  1343. }
  1344. # Truncate the privKey to the appropriate length.
  1345. my $key_len =
  1346. {
  1347. PRIV_PROTOCOL_DES, 16, # RFC 3414 Section 8.2.1
  1348. PRIV_PROTOCOL_DRAFT_3DESEDE, 32, # Draft 3DES for USM Section 5.2.1
  1349. PRIV_PROTOCOL_AESCFB128, 16, # AES in the USM Section 3.2.1
  1350. PRIV_PROTOCOL_DRAFT_AESCFB192, 24, # Draft AES in the USM Section 3.2.1
  1351. PRIV_PROTOCOL_DRAFT_AESCFB256, 32 # Draft AES in the USM Section 3.2.1
  1352. };
  1353. if (!exists $key_len->{$this->{_priv_protocol}}) {
  1354. return $this->_error(
  1355. 'The privProtocol "%s" is unknown', $this->{_priv_protocol}
  1356. );
  1357. }
  1358. $this->{_priv_key} =
  1359. substr $this->{_priv_key}, 0, $key_len->{$this->{_priv_protocol}};
  1360. return $this->{_priv_key};
  1361. }
  1362. sub _priv_key_validate
  1363. {
  1364. my ($this) = @_;
  1365. my $key_len =
  1366. {
  1367. PRIV_PROTOCOL_DES, [ 16, 'CBC-DES' ],
  1368. PRIV_PROTOCOL_DRAFT_3DESEDE, [ 32, 'CBC-3DES-EDE' ],
  1369. PRIV_PROTOCOL_AESCFB128, [ 16, 'CFB128-AES-128' ],
  1370. PRIV_PROTOCOL_DRAFT_AESCFB192, [ 24, 'CFB128-AES-192' ],
  1371. PRIV_PROTOCOL_DRAFT_AESCFB256, [ 32, 'CFB128-AES-256' ]
  1372. };
  1373. if (!exists $key_len->{$this->{_priv_protocol}}) {
  1374. return $this->_error(
  1375. 'The privProtocol "%s" is unknown', $this->{_priv_protocol}
  1376. );
  1377. }
  1378. if (length($this->{_priv_key}) != $key_len->{$this->{_priv_protocol}}->[0])
  1379. {
  1380. return $this->_error(
  1381. 'The %s privKey length of %d is invalid, expected %d',
  1382. $key_len->{$this->{_priv_protocol}}->[1], length($this->{_priv_key}),
  1383. $key_len->{$this->{_priv_protocol}}->[0]
  1384. );
  1385. }
  1386. if ($this->{_priv_protocol} eq PRIV_PROTOCOL_DRAFT_3DESEDE) {
  1387. # Draft 3DES-EDE for USM Section 5.1.1.1.1 "The checks for difference
  1388. # and weakness... ...should be performed when the key is assigned.
  1389. # If any of the mandated tests fail, then the whole key MUST be
  1390. # discarded and an appropriate exception noted."
  1391. if (substr($this->{_priv_key}, 0, 8) eq substr $this->{_priv_key}, 8, 8)
  1392. {
  1393. return $this->_error(
  1394. 'The CBC-3DES-EDE privKey is invalid (K1 equals K2)'
  1395. );
  1396. }
  1397. if (substr($this->{_priv_key}, 8, 8) eq substr $this->{_priv_key}, 16, 8)
  1398. {
  1399. return $this->_error(
  1400. 'The CBC-3DES-EDE privKey is invalid (K2 equals K3)'
  1401. );
  1402. }
  1403. if (substr($this->{_priv_key}, 0, 8) eq substr $this->{_priv_key}, 16, 8)
  1404. {
  1405. return $this->_error(
  1406. 'The CBC-3DES-EDE privKey is invalid (K1 equals K3)'
  1407. );
  1408. }
  1409. }
  1410. return TRUE;
  1411. }
  1412. sub _password_localize
  1413. {
  1414. my ($this, $password) = @_;
  1415. my $digests =
  1416. {
  1417. AUTH_PROTOCOL_HMACMD5, ['Digest::MD5', ],
  1418. AUTH_PROTOCOL_HMACSHA, ['Digest::SHA', 1],
  1419. AUTH_PROTOCOL_HMACSHA224, ['Digest::SHA', 224],
  1420. AUTH_PROTOCOL_HMACSHA256, ['Digest::SHA', 256],
  1421. AUTH_PROTOCOL_HMACSHA384, ['Digest::SHA', 384],
  1422. AUTH_PROTOCOL_HMACSHA512, ['Digest::SHA', 512],
  1423. };
  1424. if (!exists $digests->{$this->{_auth_protocol}}) {
  1425. return $this->_error(
  1426. 'The authProtocol "%s" is unknown', $this->{_auth_protocol}
  1427. );
  1428. }
  1429. my $digest;
  1430. if (!defined($digests->{$this->{_auth_protocol}}[1])) {
  1431. $digest = $digests->{$this->{_auth_protocol}}[0]->new;
  1432. } else {
  1433. $digest = $digests->{$this->{_auth_protocol}}[0]->new($digests->{$this->{_auth_protocol}}[1]);
  1434. }
  1435. # Create the initial digest using the password
  1436. my $d = my $pad = $password x ((2048 / length $password) + 1);
  1437. for (my $count = 0; $count < 2**20; $count += 2048) {
  1438. $digest->add(substr $d, 0, 2048, q{});
  1439. $d .= $pad;
  1440. }
  1441. $d = $digest->digest;
  1442. # Localize the key with the authoritativeEngineID
  1443. return $digest->add($d . $this->{_engine_id} . $d)->digest();
  1444. }
  1445. {
  1446. my %modules;
  1447. sub load_module
  1448. {
  1449. my ($module) = @_;
  1450. # We attempt to load the required module under the protection of an
  1451. # eval statement. If there is a failure, typically it is due to a
  1452. # missing module required by the requested module and we attempt to
  1453. # simplify the error message by just listing that module. We also
  1454. # need to track failures since require() only produces an error on
  1455. # the first attempt to load the module.
  1456. # NOTE: Contrary to our typical convention, a return value of "undef"
  1457. # actually means success and a defined value means error.
  1458. return $modules{$module} if exists $modules{$module};
  1459. if (!eval "require $module") {
  1460. if ($@ =~ /locate (\S+\.pm)/) {
  1461. $modules{$module} = sprintf '(Required module %s not found)', $1;
  1462. } else {
  1463. $modules{$module} = sprintf '(%s)', $@;
  1464. }
  1465. } else {
  1466. $modules{$module} = undef;
  1467. }
  1468. return $modules{$module};
  1469. }
  1470. }
  1471. # ============================================================================
  1472. 1; # [end Net::SNMP::Security::USM]