Coverage Report

Created: 2024-09-08 06:40

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