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