snmp_agent_dispatch.c
Go to the documentation of this file.
1 /**
2  * @file snmp_agent_dispatch.c
3  * @brief SNMP message dispatching
4  *
5  * @section License
6  *
7  * Copyright (C) 2010-2018 Oryx Embedded SARL. All rights reserved.
8  *
9  * This file is part of CycloneTCP Open.
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
24  *
25  * @author Oryx Embedded SARL (www.oryx-embedded.com)
26  * @version 1.9.0
27  **/
28 
29 //Switch to the appropriate trace level
30 #define TRACE_LEVEL SNMP_TRACE_LEVEL
31 
32 //Dependencies
33 #include "core/net.h"
34 #include "snmp/snmp_agent.h"
36 #include "snmp/snmp_agent_pdu.h"
37 #include "snmp/snmp_agent_misc.h"
38 #include "mibs/mib2_module.h"
39 #include "mibs/snmp_mib_module.h"
40 #include "core/crypto.h"
41 #include "encoding/asn1.h"
42 #include "encoding/oid.h"
43 #include "debug.h"
44 
45 //Check TCP/IP stack configuration
46 #if (SNMP_AGENT_SUPPORT == ENABLED)
47 
48 
49 /**
50  * @brief Process incoming SNMP message
51  * @param[in] context Pointer to the SNMP agent context
52  * @return Error code
53  **/
54 
56 {
57  error_t error;
58 
59  //Total number of messages delivered to the SNMP entity from the
60  //transport service
61  MIB2_INC_COUNTER32(snmpGroup.snmpInPkts, 1);
62  SNMP_MIB_INC_COUNTER32(snmpGroup.snmpInPkts, 1);
63 
64 #if (SNMP_V3_SUPPORT == ENABLED)
65  //Refresh SNMP engine time
66  snmpRefreshEngineTime(context);
67 #endif
68 
69  //Message parsing initialization
70  snmpInitMessage(&context->request);
71 
72  //Parse SNMP message header
73  error = snmpParseMessageHeader(&context->request);
74  //Any error to report?
75  if(error)
76  return error;
77 
78  //The SNMP agent verifies the version number. If there is a mismatch,
79  //it discards the datagram and performs no further actions
80  if(context->request.version < context->settings.versionMin ||
81  context->request.version > context->settings.versionMax)
82  {
83  //Debug message
84  TRACE_WARNING(" Invalid SNMP version!\r\n");
85  //Discard incoming SNMP message
86  return ERROR_INVALID_VERSION;
87  }
88 
89 #if (SNMP_V1_SUPPORT == ENABLED)
90  //SNMPv1 version?
91  if(context->request.version == SNMP_VERSION_1)
92  {
93  //Process incoming SNMPv1 message
94  error = snmpv1ProcessMessage(context);
95  }
96  else
97 #endif
98 #if (SNMP_V2C_SUPPORT == ENABLED)
99  //SNMPv2c version?
100  if(context->request.version == SNMP_VERSION_2C)
101  {
102  //Process incoming SNMPv2c message
103  error = snmpv2cProcessMessage(context);
104  }
105  else
106 #endif
107 #if (SNMP_V3_SUPPORT == ENABLED)
108  //SNMPv3 version?
109  if(context->request.version == SNMP_VERSION_3)
110  {
111  //Process incoming SNMPv3 message
112  error = snmpv3ProcessMessage(context);
113  }
114  else
115 #endif
116  //Invalid SNMP version?
117  {
118  //Debug message
119  TRACE_WARNING(" Invalid SNMP version!\r\n");
120 
121  //Total number of SNMP messages which were delivered to the SNMP
122  //protocol entity and were for an unsupported SNMP version
123  MIB2_INC_COUNTER32(snmpGroup.snmpInBadVersions, 1);
124  SNMP_MIB_INC_COUNTER32(snmpGroup.snmpInBadVersions, 1);
125 
126  //Discard incoming SNMP message
127  error = ERROR_INVALID_VERSION;
128  }
129 
130  //Check status code
131  if(error == NO_ERROR)
132  {
133  //Total number of messages which were passed from the SNMP protocol
134  //entity to the transport service
135  MIB2_INC_COUNTER32(snmpGroup.snmpOutPkts, 1);
136  }
137  else if(error == ERROR_INVALID_TAG)
138  {
139  //Total number of ASN.1 or BER errors encountered by the SNMP protocol
140  //entity when decoding received SNMP messages
141  MIB2_INC_COUNTER32(snmpGroup.snmpInASNParseErrs, 1);
142  SNMP_MIB_INC_COUNTER32(snmpGroup.snmpInASNParseErrs, 1);
143  }
144  else if(error == ERROR_BUFFER_OVERFLOW)
145  {
146  //Total number of PDUs delivered to the SNMP entity which were silently
147  //dropped because the size of the reply was greater than the maximum
148  //message size
149  SNMP_MIB_INC_COUNTER32(snmpGroup.snmpSilentDrops, 1);
150  }
151 
152  //Return status code
153  return error;
154 }
155 
156 
157 /**
158  * @brief Process incoming SNMPv1 message
159  * @param[in] context Pointer to the SNMP agent context
160  * @return Error code
161  **/
162 
164 {
165 #if (SNMP_V1_SUPPORT == ENABLED)
166  error_t error;
167  SnmpUserEntry *community;
168 
169  //Parse community name
170  error = snmpParseCommunity(&context->request);
171  //Any error to report?
172  if(error)
173  return error;
174 
175  //Information about the community name is extracted from the local
176  //configuration datastore
177  community = snmpFindCommunityEntry(context, context->request.community,
178  context->request.communityLen);
179 
180  //Invalid community name?
181  if(community == NULL || community->status != MIB_ROW_STATUS_ACTIVE)
182  {
183  //Debug message
184  TRACE_WARNING(" Invalid community name!\r\n");
185 
186  //Total number of SNMP messages delivered to the SNMP protocol entity
187  //which used a SNMP community name not known to said entity
188  MIB2_INC_COUNTER32(snmpGroup.snmpInBadCommunityNames, 1);
189  SNMP_MIB_INC_COUNTER32(snmpGroup.snmpInBadCommunityNames, 1);
190 
191  //Report an error
193  }
194 
195  //Save the security profile associated with the current community
196  context->user = *community;
197 
198  //Process PDU
199  error = snmpProcessPdu(context);
200  //Any error to report?
201  if(error)
202  return error;
203 
204  //Format SNMP message header
205  error = snmpWriteMessageHeader(&context->response);
206  //Return status code
207  return error;
208 #else
209  //Report an error
210  return ERROR_INVALID_VERSION;
211 #endif
212 }
213 
214 
215 /**
216  * @brief Process incoming SNMPv2c message
217  * @param[in] context Pointer to the SNMP agent context
218  * @return Error code
219  **/
220 
222 {
223 #if (SNMP_V2C_SUPPORT == ENABLED)
224  error_t error;
225  SnmpUserEntry *community;
226 
227  //Parse community name
228  error = snmpParseCommunity(&context->request);
229  //Any error to report?
230  if(error)
231  return error;
232 
233  //Information about the community name is extracted from the local
234  //configuration datastore
235  community = snmpFindCommunityEntry(context, context->request.community,
236  context->request.communityLen);
237 
238  //Invalid community name?
239  if(community == NULL || community->status != MIB_ROW_STATUS_ACTIVE)
240  {
241  //Debug message
242  TRACE_WARNING(" Invalid community name!\r\n");
243 
244  //Total number of SNMP messages delivered to the SNMP protocol entity
245  //which used a SNMP community name not known to said entity
246  MIB2_INC_COUNTER32(snmpGroup.snmpInBadCommunityNames, 1);
247  SNMP_MIB_INC_COUNTER32(snmpGroup.snmpInBadCommunityNames, 1);
248 
249  //Report an error
251  }
252 
253  //Save the security profile associated with the current community
254  context->user = *community;
255 
256  //Process PDU
257  error = snmpProcessPdu(context);
258  //Any error to report?
259  if(error)
260  return error;
261 
262  //Format SNMP message header
263  error = snmpWriteMessageHeader(&context->response);
264  //Return status code
265  return error;
266 #else
267  //Report an error
268  return ERROR_INVALID_VERSION;
269 #endif
270 }
271 
272 
273 /**
274  * @brief Process incoming SNMPv3 message
275  * @param[in] context Pointer to the SNMP agent context
276  * @return Error code
277  **/
278 
280 {
281 #if (SNMP_V3_SUPPORT == ENABLED)
282  error_t error;
283  SnmpUserEntry *user;
284 
285  //Parse msgGlobalData field
286  error = snmpParseGlobalData(&context->request);
287  //Any error to report?
288  if(error)
289  return error;
290 
291  //Parse msgSecurityParameters field
292  error = snmpParseSecurityParameters(&context->request);
293  //Any error to report?
294  if(error)
295  return error;
296 
297  //Start of exception handling block
298  do
299  {
300 #if (SNMP_AGENT_INFORM_SUPPORT == ENABLED)
301  if(context->request.msgUserNameLen == 0 && context->request.msgFlags == 0)
302  {
303  //Clear the security profile
304  memset(&context->user, 0, sizeof(SnmpUserEntry));
305  }
306  else if(context->informContextEngineLen > 0 &&
307  !oidComp(context->request.msgAuthEngineId, context->request.msgAuthEngineIdLen,
308  context->informContextEngine, context->informContextEngineLen))
309  {
310  //Information about the value of the msgUserName field is extracted
311  //from the local configuration datastore
312  user = snmpFindUserEntry(context, context->request.msgUserName,
313  context->request.msgUserNameLen);
314 
315  //Check security parameters
316  error = snmpCheckSecurityParameters(user, &context->request,
317  context->informContextEngine, context->informContextEngineLen);
318  //Invalid security parameters?
319  if(error)
320  break;
321 
322  //Save the security profile associated with the current user
323  context->user = *user;
324 
325  //Localize the authentication key with the engine ID of the
326  //remote SNMP device
327  if(context->user.authProtocol != SNMP_AUTH_PROTOCOL_NONE)
328  {
329  //Key localization algorithm
330  error = snmpLocalizeKey(context->user.authProtocol,
331  context->informContextEngine, context->informContextEngineLen,
332  &context->user.rawAuthKey, &context->user.localizedAuthKey);
333  //Any error to report?
334  if(error)
335  break;
336  }
337 
338  //Localize the privacy key with the engine ID of the remote
339  //SNMP device
340  if(context->user.privProtocol != SNMP_AUTH_PROTOCOL_NONE)
341  {
342  //Key localization algorithm
343  error = snmpLocalizeKey(context->user.authProtocol,
344  context->informContextEngine, context->informContextEngineLen,
345  &context->user.rawPrivKey, &context->user.localizedPrivKey);
346  //Any error to report?
347  if(error)
348  break;
349  }
350  }
351  else
352 #endif
353  {
354  //Information about the value of the msgUserName field is extracted
355  //from the local configuration datastore
356  user = snmpFindUserEntry(context, context->request.msgUserName,
357  context->request.msgUserNameLen);
358 
359  //Check security parameters
360  error = snmpCheckSecurityParameters(user, &context->request,
361  context->contextEngine, context->contextEngineLen);
362  //Invalid security parameters?
363  if(error)
364  break;
365 
366  //Save the security profile associated with the current user
367  context->user = *user;
368  }
369 
370  //Check whether the authFlag is set
371  if(context->request.msgFlags & SNMP_MSG_FLAG_AUTH)
372  {
373  //Authenticate incoming SNMP message
374  error = snmpAuthIncomingMessage(&context->user, &context->request);
375  //Data authentication failed?
376  if(error)
377  break;
378 
379  //Replay protection
380  error = snmpCheckEngineTime(context, &context->request);
381  //Message outside of the time window?
382  if(error)
383  break;
384  }
385 
386  //Check whether the privFlag is set
387  if(context->request.msgFlags & SNMP_MSG_FLAG_PRIV)
388  {
389  //Decrypt data
390  error = snmpDecryptData(&context->user, &context->request);
391  //Data decryption failed?
392  if(error)
393  break;
394  }
395 
396  //Parse scopedPDU
397  error = snmpParseScopedPdu(&context->request);
398  //Any error to report?
399  if(error)
400  break;
401 
402  //Process PDU
403  error = snmpProcessPdu(context);
404  //Any error to report?
405  if(error)
406  break;
407 
408  //End of exception handling block
409  } while(0);
410 
411  //Check error indication
412  if(error == ERROR_UNSUPPORTED_SECURITY_LEVEL ||
413  error == ERROR_NOT_IN_TIME_WINDOW ||
414  error == ERROR_UNKNOWN_USER_NAME ||
415  error == ERROR_UNKNOWN_ENGINE_ID ||
416  error == ERROR_AUTHENTICATION_FAILED ||
417  error == ERROR_DECRYPTION_FAILED ||
418  error == ERROR_UNAVAILABLE_CONTEXT ||
419  error == ERROR_UNKNOWN_CONTEXT)
420  {
421  //When the reportable flag is used, if its value is one, a Report-PDU
422  //must be returned to the sender
423  if(context->request.msgFlags & SNMP_MSG_FLAG_REPORTABLE)
424  error = snmpFormatReportPdu(context, error);
425 
426  //Any error to report?
427  if(error)
428  return error;
429  }
430  else if(error == NO_ERROR)
431  {
432  //Continue processing
433  }
434  else
435  {
436  //Stop processing
437  return error;
438  }
439 
440  //Any response?
441  if(context->response.length > 0)
442  {
443  //Format scopedPDU
444  error = snmpWriteScopedPdu(&context->response);
445  //Any error to report?
446  if(error)
447  return error;
448 
449  //Check whether the privFlag is set
450  if(context->response.msgFlags & SNMP_MSG_FLAG_PRIV)
451  {
452  //Encrypt data
453  error = snmpEncryptData(&context->user, &context->response,
454  &context->salt);
455  //Any error to report?
456  if(error)
457  return error;
458  }
459 
460  //Format SNMP message header
461  error = snmpWriteMessageHeader(&context->response);
462  //Any error to report?
463  if(error)
464  return error;
465 
466  //Check whether the authFlag is set
467  if(context->response.msgFlags & SNMP_MSG_FLAG_AUTH)
468  {
469  //Authenticate outgoing SNMP message
470  error = snmpAuthOutgoingMessage(&context->user, &context->response);
471  //Any error to report?
472  if(error)
473  return error;
474  }
475  }
476 
477  //Successful processing
478  return NO_ERROR;
479 #else
480  //Report an error
481  return ERROR_INVALID_VERSION;
482 #endif
483 }
484 
485 #endif
SnmpUserEntry * snmpFindUserEntry(SnmpAgentContext *context, const char_t *name, size_t length)
Search the user table for a given user name.
error_t snmpParseCommunity(SnmpMessage *message)
Parse community name.
MibRowStatus status
Status of the user.
error_t snmpCheckSecurityParameters(const SnmpUserEntry *user, SnmpMessage *message, const uint8_t *engineId, size_t engineIdLen)
Check security parameters.
void snmpRefreshEngineTime(SnmpAgentContext *context)
Refresh SNMP engine time.
error_t snmpAuthIncomingMessage(const SnmpUserEntry *user, SnmpMessage *message)
Authenticate incoming SNMP message.
TCP/IP stack core.
Debugging facilities.
error_t snmpProcessMessage(SnmpAgentContext *context)
Process incoming SNMP message.
MIB-II module.
General definitions for cryptographic algorithms.
error_t snmpv2cProcessMessage(SnmpAgentContext *context)
Process incoming SNMPv2c message.
error_t snmpWriteMessageHeader(SnmpMessage *message)
Format SNMP message header.
error_t snmpv1ProcessMessage(SnmpAgentContext *context)
Process incoming SNMPv1 message.
SnmpUserEntry * snmpFindCommunityEntry(SnmpAgentContext *context, const char_t *community, size_t length)
Search the community table for a given community string.
#define MIB2_INC_COUNTER32(name, value)
Definition: mib2_module.h:154
User table entry.
SNMP MIB module.
OID (Object Identifier)
error_t snmpLocalizeKey(SnmpAuthProtocol authProtocol, const uint8_t *engineId, size_t engineIdLen, SnmpKey *key, SnmpKey *localizedKey)
Key localization algorithm.
error_t snmpProcessPdu(SnmpAgentContext *context)
Process PDU.
ASN.1 (Abstract Syntax Notation One)
SNMP agent (Simple Network Management Protocol)
Helper functions for SNMP agent.
error_t snmpEncryptData(const SnmpUserEntry *user, SnmpMessage *message, uint64_t *salt)
Data encryption.
SNMP agent (PDU processing)
int_t oidComp(const uint8_t *oid1, size_t oidLen1, const uint8_t *oid2, size_t oidLen2)
Compare object identifiers.
Definition: oid.c:99
Success.
Definition: error.h:42
error_t
Error codes.
Definition: error.h:40
error_t snmpAuthOutgoingMessage(const SnmpUserEntry *user, SnmpMessage *message)
Authenticate outgoing SNMP message.
#define TRACE_WARNING(...)
Definition: debug.h:78
error_t snmpParseSecurityParameters(SnmpMessage *message)
Parse msgSecurityParameters field.
error_t snmpv3ProcessMessage(SnmpAgentContext *context)
Process incoming SNMPv3 message.
error_t snmpDecryptData(const SnmpUserEntry *user, SnmpMessage *message)
Data decryption.
#define SnmpAgentContext
Definition: snmp_agent.h:34
No authentication.
error_t snmpParseGlobalData(SnmpMessage *message)
Parse msgGlobalData field.
error_t snmpParseScopedPdu(SnmpMessage *message)
Parse scopedPDU field.
error_t snmpCheckEngineTime(SnmpAgentContext *context, SnmpMessage *message)
Replay protection.
SNMP message dispatching.
error_t snmpWriteScopedPdu(SnmpMessage *message)
Format scopedPDU.
#define SNMP_MIB_INC_COUNTER32(name, value)
void snmpInitMessage(SnmpMessage *message)
Initialize a SNMP message.
error_t snmpFormatReportPdu(SnmpAgentContext *context, error_t errorIndication)
Format Report-PDU.
error_t snmpParseMessageHeader(SnmpMessage *message)
Parse SNMP message header.