1 /*
2  *  AgentX Administrative request handling
3  */
4 #include <net-snmp/net-snmp-config.h>
5 #include <net-snmp/net-snmp-features.h>
6 
7 #include <sys/types.h>
8 #ifdef HAVE_STRING_H
9 #include <string.h>
10 #else
11 #include <strings.h>
12 #endif
13 #ifdef HAVE_STDLIB_H
14 #include <stdlib.h>
15 #endif
16 #if TIME_WITH_SYS_TIME
17 # include <sys/time.h>
18 # include <time.h>
19 #else
20 # if HAVE_SYS_TIME_H
21 #  include <sys/time.h>
22 # else
23 #  include <time.h>
24 # endif
25 #endif
26 #if HAVE_NETINET_IN_H
27 #include <netinet/in.h>
28 #endif
29 #if HAVE_SYS_SOCKET_H
30 #include <sys/socket.h>
31 #endif
32 
33 #include <net-snmp/net-snmp-includes.h>
34 #include <net-snmp/agent/net-snmp-agent-includes.h>
35 #include "agent_global_vars.h"
36 
37 #include "agentx/protocol.h"
38 #include "agentx/client.h"
39 #include "agentx/subagent.h"
40 #include "agentx/master_admin.h"
41 
42 #include <net-snmp/agent/agent_index.h>
43 #include <net-snmp/agent/agent_trap.h>
44 #include <net-snmp/agent/agent_callbacks.h>
45 #include <net-snmp/agent/agent_sysORTable.h>
46 #include "master.h"
47 
48 netsnmp_feature_require(unregister_mib_table_row);
49 netsnmp_feature_require(trap_vars_with_context);
50 netsnmp_feature_require(calculate_sectime_diff);
51 netsnmp_feature_require(allocate_globalcacheid);
52 netsnmp_feature_require(remove_index);
53 
54 netsnmp_session *
find_agentx_session(netsnmp_session * session,int sessid)55 find_agentx_session(netsnmp_session * session, int sessid)
56 {
57     netsnmp_session *sp;
58     for (sp = session->subsession; sp != NULL; sp = sp->next) {
59         if (sp->sessid == sessid)
60             return sp;
61     }
62     return NULL;
63 }
64 
65 
66 int
open_agentx_session(netsnmp_session * session,netsnmp_pdu * pdu)67 open_agentx_session(netsnmp_session * session, netsnmp_pdu *pdu)
68 {
69     netsnmp_session *sp;
70 
71     DEBUGMSGTL(("agentx/master", "open %8p\n", session));
72     sp = (netsnmp_session *) malloc(sizeof(netsnmp_session));
73     if (sp == NULL) {
74         session->s_snmp_errno = AGENTX_ERR_OPEN_FAILED;
75         return -1;
76     }
77 
78     memcpy(sp, session, sizeof(netsnmp_session));
79     sp->sessid = snmp_get_next_sessid();
80     sp->version = pdu->version;
81     sp->timeout = pdu->time;
82 
83     /*
84      * Be careful with fields: if these aren't zeroed, they will get free()d
85      * more than once when the session is closed -- once in the main session,
86      * and once in each subsession.  Basically, if it's not being used for
87      * some AgentX-specific purpose, it ought to be zeroed here.
88      */
89 
90     sp->community = NULL;
91     sp->peername = NULL;
92     sp->contextEngineID = NULL;
93     sp->contextName = NULL;
94     sp->securityEngineID = NULL;
95     sp->securityPrivProto = NULL;
96 
97     /*
98      * This next bit utilises unused SNMPv3 fields
99      *   to store the subagent OID and description.
100      * This really ought to use AgentX-specific fields,
101      *   but it hardly seems worth it for a one-off use.
102      *
103      * But I'm willing to be persuaded otherwise....  */
104     sp->securityAuthProto = snmp_duplicate_objid(pdu->variables->name,
105                                                  pdu->variables->
106                                                  name_length);
107     sp->securityAuthProtoLen = pdu->variables->name_length;
108     sp->securityName = strdup((char *) pdu->variables->val.string);
109     sp->engineTime = (uint32_t)((netsnmp_get_agent_runtime() + 50) / 100) & 0x7fffffffL;
110 
111     sp->subsession = session;   /* link back to head */
112     sp->flags |= SNMP_FLAGS_SUBSESSION;
113     sp->flags &= ~AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER;
114     sp->flags |= (pdu->flags & AGENTX_MSG_FLAG_NETWORK_BYTE_ORDER);
115     sp->next = session->subsession;
116     session->subsession = sp;
117     DEBUGMSGTL(("agentx/master", "opened %8p = %ld with flags = %02lx\n",
118                 sp, sp->sessid, sp->flags & AGENTX_MSG_FLAGS_MASK));
119 
120     return sp->sessid;
121 }
122 
123 int
close_agentx_session(netsnmp_session * session,int sessid)124 close_agentx_session(netsnmp_session * session, int sessid)
125 {
126     netsnmp_session *sp, **prevNext;
127 
128     if (!session)
129         return AGENTX_ERR_NOT_OPEN;
130 
131     DEBUGMSGTL(("agentx/master", "close %8p, %d\n", session, sessid));
132     if (sessid == -1) {
133         /*
134          * The following is necessary to avoid locking up the agent when
135          * a sugagent dies during a set request. We must clean up the
136          * requests, so that the delegated request will be completed and
137          * further requests can be processed
138          */
139 	while (netsnmp_remove_delegated_requests_for_session(session)) {
140 		DEBUGMSGTL(("agentx/master", "Continue removing delegated reqests\n"));
141 	}
142 
143         if (session->subsession != NULL) {
144             netsnmp_session *subsession = session->subsession;
145             for(; subsession; subsession = subsession->next) {
146                 while (netsnmp_remove_delegated_requests_for_session(subsession)) {
147 			DEBUGMSGTL(("agentx/master", "Continue removing delegated subsession reqests\n"));
148 		}
149             }
150         }
151 
152         unregister_mibs_by_session(session);
153         unregister_index_by_session(session);
154         unregister_sysORTable_by_session(session);
155 	SNMP_FREE(session->myvoid);
156         return AGENTX_ERR_NOERROR;
157     }
158 
159     prevNext = &(session->subsession);
160 
161     for (sp = session->subsession; sp != NULL; sp = sp->next) {
162 
163         if (sp->sessid == sessid) {
164             netsnmp_remove_delegated_requests_for_session(sp);
165             unregister_mibs_by_session(sp);
166             unregister_index_by_session(sp);
167             unregister_sysORTable_by_session(sp);
168 
169             *prevNext = sp->next;
170 
171             if (sp->securityAuthProto != NULL) {
172                 free(sp->securityAuthProto);
173             }
174             if (sp->securityName != NULL) {
175                 free(sp->securityName);
176             }
177             free(sp);
178             sp = NULL;
179 
180             DEBUGMSGTL(("agentx/master", "closed %8p, %d okay\n",
181                         session, sessid));
182             return AGENTX_ERR_NOERROR;
183         }
184 
185         prevNext = &(sp->next);
186     }
187 
188     DEBUGMSGTL(("agentx/master", "sessid %d not found\n", sessid));
189     return AGENTX_ERR_NOT_OPEN;
190 }
191 
192 int
register_agentx_list(netsnmp_session * session,netsnmp_pdu * pdu)193 register_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu)
194 {
195     netsnmp_session *sp;
196     char            buf[128];
197     oid             ubound = 0;
198     u_long          flags = 0;
199     netsnmp_handler_registration *reg;
200     int             rc = 0;
201     int             cacheid;
202 
203     DEBUGMSGTL(("agentx/master", "in register_agentx_list\n"));
204 
205     sp = find_agentx_session(session, pdu->sessid);
206     if (sp == NULL)
207         return AGENTX_ERR_NOT_OPEN;
208 
209     sprintf(buf, "AgentX subagent %ld, session %8p, subsession %8p",
210             sp->sessid, session, sp);
211     /*
212      * * TODO: registration timeout
213      * *   registration context
214      */
215     if (pdu->range_subid) {
216         ubound = pdu->variables->val.objid[pdu->range_subid - 1];
217     }
218 
219     if (pdu->flags & AGENTX_MSG_FLAG_INSTANCE_REGISTER) {
220         flags = FULLY_QUALIFIED_INSTANCE;
221     }
222 
223     reg = netsnmp_create_handler_registration(buf, agentx_master_handler, pdu->variables->name, pdu->variables->name_length, HANDLER_CAN_RWRITE | HANDLER_CAN_GETBULK); /* fake it */
224     if (!session->myvoid) {
225         session->myvoid = malloc(sizeof(cacheid));
226         cacheid = netsnmp_allocate_globalcacheid();
227         *((int *) session->myvoid) = cacheid;
228     } else {
229         cacheid = *((int *) session->myvoid);
230     }
231 
232     reg->handler->myvoid = session;
233     reg->global_cacheid = cacheid;
234     if (NULL != pdu->community)
235         reg->contextName = strdup((char *)pdu->community);
236 
237     /*
238      * register mib. Note that for failure cases, the registration info
239      * (reg) will be freed, and thus is no longer a valid pointer.
240      */
241     switch (netsnmp_register_mib(buf, NULL, 0, 0,
242                                  pdu->variables->name,
243                                  pdu->variables->name_length,
244                                  pdu->priority, pdu->range_subid, ubound,
245                                  sp, (char *) pdu->community, pdu->time,
246                                  flags, reg, 1)) {
247 
248     case MIB_REGISTERED_OK:
249         DEBUGMSGTL(("agentx/master", "registered ok\n"));
250         return AGENTX_ERR_NOERROR;
251 
252     case MIB_DUPLICATE_REGISTRATION:
253         DEBUGMSGTL(("agentx/master", "duplicate registration\n"));
254         rc = AGENTX_ERR_DUPLICATE_REGISTRATION;
255         break;
256 
257     case MIB_REGISTRATION_FAILED:
258     default:
259         rc = AGENTX_ERR_REQUEST_DENIED;
260         DEBUGMSGTL(("agentx/master", "failed registration\n"));
261     }
262     return rc;
263 }
264 
265 int
unregister_agentx_list(netsnmp_session * session,netsnmp_pdu * pdu)266 unregister_agentx_list(netsnmp_session * session, netsnmp_pdu *pdu)
267 {
268     netsnmp_session *sp;
269     int             rc = 0;
270 
271     sp = find_agentx_session(session, pdu->sessid);
272     if (sp == NULL) {
273         return AGENTX_ERR_NOT_OPEN;
274     }
275 
276     if (pdu->range_subid != 0) {
277         oid             ubound =
278             pdu->variables->val.objid[pdu->range_subid - 1];
279         rc = netsnmp_unregister_mib_table_row(pdu->variables->name,
280                                               pdu->variables->name_length,
281                                               pdu->priority,
282                                               pdu->range_subid, ubound,
283                                               (char *) pdu->community);
284     } else {
285         rc = unregister_mib_context(pdu->variables->name,
286                                     pdu->variables->name_length,
287                                     pdu->priority, 0, 0,
288                                     (char *) pdu->community);
289     }
290 
291     switch (rc) {
292     case MIB_UNREGISTERED_OK:
293         return AGENTX_ERR_NOERROR;
294     case MIB_NO_SUCH_REGISTRATION:
295         return AGENTX_ERR_UNKNOWN_REGISTRATION;
296     case MIB_UNREGISTRATION_FAILED:
297     default:
298         return AGENTX_ERR_REQUEST_DENIED;
299     }
300 }
301 
302 int
allocate_idx_list(netsnmp_session * session,netsnmp_pdu * pdu)303 allocate_idx_list(netsnmp_session * session, netsnmp_pdu *pdu)
304 {
305     netsnmp_session *sp;
306     netsnmp_variable_list *vp, *vp2, *next, *res;
307     int             flags = 0;
308 
309     sp = find_agentx_session(session, pdu->sessid);
310     if (sp == NULL)
311         return AGENTX_ERR_NOT_OPEN;
312 
313     if (pdu->flags & AGENTX_MSG_FLAG_ANY_INSTANCE)
314         flags |= ALLOCATE_ANY_INDEX;
315     if (pdu->flags & AGENTX_MSG_FLAG_NEW_INSTANCE)
316         flags |= ALLOCATE_NEW_INDEX;
317 
318     /*
319      * XXX - what about errors?
320      *
321      *  If any allocations fail, then we need to
322      *    *fully* release the earlier ones.
323      *  (i.e. remove them completely from the index registry,
324      *    not simply mark them as available for re-use)
325      *
326      * For now - assume they all succeed.
327      */
328     for (vp = pdu->variables; vp != NULL; vp = next) {
329         next = vp->next_variable;
330         res = register_index(vp, flags, session);
331         if (res == NULL) {
332             /*
333              *  If any allocations fail, we need to *fully* release
334              *      all previous ones (i.e. remove them completely
335              *      from the index registry)
336              */
337             for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) {
338                 remove_index(vp2, session);
339             }
340             return AGENTX_ERR_INDEX_NONE_AVAILABLE;     /* XXX */
341         } else {
342             (void) snmp_clone_var(res, vp);
343             free(res);
344         }
345         vp->next_variable = next;
346     }
347     return AGENTX_ERR_NOERROR;
348 }
349 
350 int
release_idx_list(netsnmp_session * session,netsnmp_pdu * pdu)351 release_idx_list(netsnmp_session * session, netsnmp_pdu *pdu)
352 {
353     netsnmp_session *sp;
354     netsnmp_variable_list *vp, *vp2, *rv = NULL;
355     int             res;
356 
357     sp = find_agentx_session(session, pdu->sessid);
358     if (sp == NULL)
359         return AGENTX_ERR_NOT_OPEN;
360 
361     for (vp = pdu->variables; vp != NULL; vp = vp->next_variable) {
362         res = unregister_index(vp, TRUE, session);
363         /*
364          *  If any releases fail,
365          *      we need to reinstate all previous ones.
366          */
367         if (res != SNMP_ERR_NOERROR) {
368             for (vp2 = pdu->variables; vp2 != vp; vp2 = vp2->next_variable) {
369                 rv = register_index(vp2, ALLOCATE_THIS_INDEX, session);
370                 free(rv);
371             }
372             return AGENTX_ERR_INDEX_NOT_ALLOCATED;      /* Probably */
373         }
374     }
375     return AGENTX_ERR_NOERROR;
376 }
377 
378 int
add_agent_caps_list(netsnmp_session * session,netsnmp_pdu * pdu)379 add_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu)
380 {
381     netsnmp_session *sp;
382     char* description;
383 
384     sp = find_agentx_session(session, pdu->sessid);
385     if (sp == NULL)
386         return AGENTX_ERR_NOT_OPEN;
387 
388     description = netsnmp_strdup_and_null(pdu->variables->val.string,
389                                           pdu->variables->val_len);
390     register_sysORTable_sess(pdu->variables->name, pdu->variables->name_length,
391                              description, sp);
392     free(description);
393     return AGENTX_ERR_NOERROR;
394 }
395 
396 int
remove_agent_caps_list(netsnmp_session * session,netsnmp_pdu * pdu)397 remove_agent_caps_list(netsnmp_session * session, netsnmp_pdu *pdu)
398 {
399     netsnmp_session *sp;
400     int rc;
401 
402     sp = find_agentx_session(session, pdu->sessid);
403     if (sp == NULL)
404         return AGENTX_ERR_NOT_OPEN;
405 
406     rc = unregister_sysORTable_sess(pdu->variables->name,
407                                     pdu->variables->name_length, sp);
408 
409     if (rc < 0)
410       return AGENTX_ERR_UNKNOWN_AGENTCAPS;
411 
412     return AGENTX_ERR_NOERROR;
413 }
414 
415 int
agentx_notify(netsnmp_session * session,netsnmp_pdu * pdu)416 agentx_notify(netsnmp_session * session, netsnmp_pdu *pdu)
417 {
418     netsnmp_session       *sp;
419     netsnmp_variable_list *var;
420 
421     sp = find_agentx_session(session, pdu->sessid);
422     if (sp == NULL)
423         return AGENTX_ERR_NOT_OPEN;
424 
425     var = pdu->variables;
426     if (!var)
427         return AGENTX_ERR_PROCESSING_ERROR;
428 
429     if (snmp_oid_compare(var->name, var->name_length,
430                          sysuptime_oid, sysuptime_oid_len) == 0) {
431         var = var->next_variable;
432     }
433 
434     if (!var || snmp_oid_compare(var->name, var->name_length,
435                                  snmptrap_oid, snmptrap_oid_len) != 0)
436         return AGENTX_ERR_PROCESSING_ERROR;
437 
438     /*
439      *  If sysUptime isn't the first varbind, don't worry.
440      *     send_trap_vars() will add it if necessary.
441      *
442      *  Note that if this behaviour is altered, it will
443      *     be necessary to add sysUptime here,
444      *     as this is valid AgentX syntax.
445      */
446 
447     /* If a context name was specified, send the trap using that context.
448      * Otherwise, send the trap without the context using the old method */
449     if (pdu->contextName != NULL)
450         send_trap_vars_with_context(-1, -1, pdu->variables, pdu->contextName);
451     else
452         send_trap_vars(-1, -1, pdu->variables);
453 
454     return AGENTX_ERR_NOERROR;
455 }
456 
457 
458 int
agentx_ping_response(netsnmp_session * session,netsnmp_pdu * pdu)459 agentx_ping_response(netsnmp_session * session, netsnmp_pdu *pdu)
460 {
461     netsnmp_session *sp;
462 
463     sp = find_agentx_session(session, pdu->sessid);
464     if (sp == NULL)
465         return AGENTX_ERR_NOT_OPEN;
466     else
467         return AGENTX_ERR_NOERROR;
468 }
469 
470 int
handle_master_agentx_packet(int operation,netsnmp_session * session,int reqid,netsnmp_pdu * pdu,void * magic)471 handle_master_agentx_packet(int operation,
472                             netsnmp_session * session,
473                             int reqid, netsnmp_pdu *pdu, void *magic)
474 {
475     netsnmp_agent_session *asp;
476 
477     if (operation == NETSNMP_CALLBACK_OP_DISCONNECT) {
478         DEBUGMSGTL(("agentx/master",
479                     "transport disconnect on session %8p\n", session));
480         /*
481          * Shut this session down gracefully.
482          */
483         close_agentx_session(session, -1);
484         return 1;
485     } else if (operation == NETSNMP_CALLBACK_OP_CONNECT) {
486         DEBUGMSGTL(("agentx/master",
487                     "transport connect on session %8p\n", session));
488         return 1;
489     } else if (operation != NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE) {
490         DEBUGMSGTL(("agentx/master", "unexpected callback op %d\n",
491                     operation));
492         return 1;
493     }
494 
495     /*
496      * Okay, it's a NETSNMP_CALLBACK_OP_RECEIVED_MESSAGE op.
497      */
498 
499     if (magic) {
500         asp = (netsnmp_agent_session *) magic;
501     } else {
502         asp = init_agent_snmp_session(session, pdu);
503     }
504 
505     DEBUGMSGTL(("agentx/master", "handle pdu (req=0x%lx,trans=0x%lx,sess=0x%lx)\n",
506                 (unsigned long)pdu->reqid, (unsigned long)pdu->transid,
507 		(unsigned long)pdu->sessid));
508 
509     switch (pdu->command) {
510     case AGENTX_MSG_OPEN:
511         asp->pdu->sessid = open_agentx_session(session, pdu);
512         if (asp->pdu->sessid == -1)
513             asp->status = session->s_snmp_errno;
514         break;
515 
516     case AGENTX_MSG_CLOSE:
517         asp->status = close_agentx_session(session, pdu->sessid);
518         break;
519 
520     case AGENTX_MSG_REGISTER:
521         asp->status = register_agentx_list(session, pdu);
522         break;
523 
524     case AGENTX_MSG_UNREGISTER:
525         asp->status = unregister_agentx_list(session, pdu);
526         break;
527 
528     case AGENTX_MSG_INDEX_ALLOCATE:
529         asp->status = allocate_idx_list(session, asp->pdu);
530         if (asp->status != AGENTX_ERR_NOERROR) {
531             snmp_free_pdu(asp->pdu);
532             asp->pdu = snmp_clone_pdu(pdu);
533         }
534         break;
535 
536     case AGENTX_MSG_INDEX_DEALLOCATE:
537         asp->status = release_idx_list(session, pdu);
538         break;
539 
540     case AGENTX_MSG_ADD_AGENT_CAPS:
541         asp->status = add_agent_caps_list(session, pdu);
542         break;
543 
544     case AGENTX_MSG_REMOVE_AGENT_CAPS:
545         asp->status = remove_agent_caps_list(session, pdu);
546         break;
547 
548     case AGENTX_MSG_NOTIFY:
549         asp->status = agentx_notify(session, pdu);
550         break;
551 
552     case AGENTX_MSG_PING:
553         asp->status = agentx_ping_response(session, pdu);
554         break;
555 
556         /*
557          * TODO: Other admin packets
558          */
559 
560     case AGENTX_MSG_GET:
561     case AGENTX_MSG_GETNEXT:
562     case AGENTX_MSG_GETBULK:
563     case AGENTX_MSG_TESTSET:
564     case AGENTX_MSG_COMMITSET:
565     case AGENTX_MSG_UNDOSET:
566     case AGENTX_MSG_CLEANUPSET:
567     case AGENTX_MSG_RESPONSE:
568         /*
569          * Shouldn't be handled here
570          */
571         break;
572 
573     default:
574         asp->status = AGENTX_ERR_PARSE_FAILED;
575         break;
576     }
577 
578     asp->pdu->time = netsnmp_get_agent_uptime();
579     asp->pdu->command = AGENTX_MSG_RESPONSE;
580     asp->pdu->errstat = asp->status;
581     DEBUGMSGTL(("agentx/master", "send response, stat %d (req=0x%lx,trans="
582                 "0x%lx,sess=0x%lx)\n",
583                 asp->status, (unsigned long)pdu->reqid,
584 		(unsigned long)pdu->transid, (unsigned long)pdu->sessid));
585     if (!snmp_send(asp->session, asp->pdu)) {
586         char           *eb = NULL;
587         int             pe, pse;
588         snmp_error(asp->session, &pe, &pse, &eb);
589         snmp_free_pdu(asp->pdu);
590         DEBUGMSGTL(("agentx/master", "FAILED %d %d %s\n", pe, pse, eb));
591         free(eb);
592     }
593     asp->pdu = NULL;
594     free_agent_snmp_session(asp);
595 
596     return 1;
597 }
598