1 /*
2  * baby_steps.c
3  * $Id$
4  *
5  * Portions of this file are copyrighted by:
6  * Copyright (c) 2016 VMware, Inc. All rights reserved.
7  * Use is subject to license terms specified in the COPYING file
8  * distributed with the Net-SNMP package.
9  */
10 #include <net-snmp/net-snmp-config.h>
11 #include <net-snmp/net-snmp-features.h>
12 #include <net-snmp/net-snmp-includes.h>
13 #include <net-snmp/agent/net-snmp-agent-includes.h>
14 
15 netsnmp_feature_provide(baby_steps);
16 netsnmp_feature_child_of(baby_steps, mib_helpers);
17 
18 #ifdef NETSNMP_FEATURE_REQUIRE_BABY_STEPS
19 netsnmp_feature_require(check_requests_error);
20 #endif
21 
22 #ifndef NETSNMP_FEATURE_REMOVE_BABY_STEPS
23 
24 #include <net-snmp/agent/baby_steps.h>
25 
26 #define BABY_STEPS_PER_MODE_MAX     4
27 #define BSTEP_USE_ORIGINAL          0xffff
28 
29 static u_short get_mode_map[BABY_STEPS_PER_MODE_MAX] = {
30     MODE_BSTEP_PRE_REQUEST, MODE_BSTEP_OBJECT_LOOKUP, BSTEP_USE_ORIGINAL, MODE_BSTEP_POST_REQUEST };
31 
32 #ifndef NETSNMP_NO_WRITE_SUPPORT
33 static u_short set_mode_map[SNMP_MSG_INTERNAL_SET_MAX][BABY_STEPS_PER_MODE_MAX] = {
34     /*R1*/
35     { MODE_BSTEP_PRE_REQUEST, MODE_BSTEP_OBJECT_LOOKUP, MODE_BSTEP_ROW_CREATE,
36       MODE_BSTEP_CHECK_VALUE },
37     /*R2*/
38     { MODE_BSTEP_UNDO_SETUP, BABY_STEP_NONE, BABY_STEP_NONE, BABY_STEP_NONE },
39     /*A */
40     { MODE_BSTEP_SET_VALUE,MODE_BSTEP_CHECK_CONSISTENCY,
41       MODE_BSTEP_COMMIT, BABY_STEP_NONE },
42     /*C */
43     { MODE_BSTEP_IRREVERSIBLE_COMMIT, MODE_BSTEP_UNDO_CLEANUP, MODE_BSTEP_POST_REQUEST,
44       BABY_STEP_NONE},
45     /*F */
46     { MODE_BSTEP_UNDO_CLEANUP, MODE_BSTEP_POST_REQUEST, BABY_STEP_NONE,
47       BABY_STEP_NONE },
48     /*U */
49     { MODE_BSTEP_UNDO_COMMIT, MODE_BSTEP_UNDO_SET, MODE_BSTEP_UNDO_CLEANUP,
50       MODE_BSTEP_POST_REQUEST}
51 };
52 #endif /* NETSNMP_NO_WRITE_SUPPORT */
53 
54 static int
55 _baby_steps_helper(netsnmp_mib_handler *handler,
56                    netsnmp_handler_registration *reginfo,
57                    netsnmp_agent_request_info *reqinfo,
58                    netsnmp_request_info *requests);
59 static int
60 _baby_steps_access_multiplexer(netsnmp_mib_handler *handler,
61                                netsnmp_handler_registration *reginfo,
62                                netsnmp_agent_request_info *reqinfo,
63                                netsnmp_request_info *requests);
64 
65 /** @defgroup baby_steps baby_steps
66  *  Calls your handler in baby_steps for set processing.
67  *  @ingroup handler
68  *  @{
69  */
70 
71 static netsnmp_baby_steps_modes *
netsnmp_baby_steps_modes_ref(netsnmp_baby_steps_modes * md)72 netsnmp_baby_steps_modes_ref(netsnmp_baby_steps_modes *md)
73 {
74     md->refcnt++;
75     return md;
76 }
77 
78 static void
netsnmp_baby_steps_modes_deref(netsnmp_baby_steps_modes * md)79 netsnmp_baby_steps_modes_deref(netsnmp_baby_steps_modes *md)
80 {
81     if (--md->refcnt == 0)
82 	free(md);
83 }
84 
85 /** returns a baby_steps handler that can be injected into a given
86  *  handler chain.
87  */
88 netsnmp_mib_handler *
netsnmp_baby_steps_handler_get(u_long modes)89 netsnmp_baby_steps_handler_get(u_long modes)
90 {
91     netsnmp_mib_handler *mh;
92     netsnmp_baby_steps_modes *md;
93 
94     mh = netsnmp_create_handler("baby_steps", _baby_steps_helper);
95     if(!mh)
96         return NULL;
97 
98     md = SNMP_MALLOC_TYPEDEF(netsnmp_baby_steps_modes);
99     if (NULL == md) {
100         snmp_log(LOG_ERR,"malloc failed in netsnmp_baby_steps_handler_get\n");
101         netsnmp_handler_free(mh);
102         mh = NULL;
103     }
104     else {
105 	md->refcnt = 1;
106         mh->myvoid = md;
107 	mh->data_clone = (void *(*)(void *))netsnmp_baby_steps_modes_ref;
108 	mh->data_free = (void (*)(void *))netsnmp_baby_steps_modes_deref;
109         if (0 == modes)
110             modes = BABY_STEP_ALL;
111         md->registered = modes;
112     }
113 
114     /*
115      * don't set MIB_HANDLER_AUTO_NEXT, since we need to call lower
116      * handlers with a munged mode.
117      */
118 
119     return mh;
120 }
121 
122 /** @internal Implements the baby_steps handler */
123 static int
_baby_steps_helper(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)124 _baby_steps_helper(netsnmp_mib_handler *handler,
125                          netsnmp_handler_registration *reginfo,
126                          netsnmp_agent_request_info *reqinfo,
127                          netsnmp_request_info *requests)
128 {
129     netsnmp_baby_steps_modes *bs_modes;
130     int save_mode, i, rc = SNMP_ERR_NOERROR;
131     u_short *mode_map_ptr;
132 
133     DEBUGMSGTL(("baby_steps", "Got request, mode %s\n",
134                 se_find_label_in_slist("agent_mode",reqinfo->mode)));
135 
136     bs_modes = (netsnmp_baby_steps_modes*)handler->myvoid;
137     netsnmp_assert(NULL != bs_modes);
138 
139     switch (reqinfo->mode) {
140 
141 #ifndef NETSNMP_NO_WRITE_SUPPORT
142     case MODE_SET_RESERVE1:
143         /*
144          * clear completed modes
145          * xxx-rks: this will break for pdus with set requests to different
146          * rows in the same table when the handler is set up to use the row
147          * merge helper as well (or if requests are serialized).
148          */
149         bs_modes->completed = 0;
150         /* FALL THROUGH */
151     case MODE_SET_RESERVE2:
152     case MODE_SET_ACTION:
153     case MODE_SET_COMMIT:
154     case MODE_SET_FREE:
155     case MODE_SET_UNDO:
156         mode_map_ptr = set_mode_map[reqinfo->mode];
157         break;
158 #endif /* NETSNMP_NO_WRITE_SUPPORT */
159 
160     default:
161         /*
162          * clear completed modes
163          */
164         bs_modes->completed = 0;
165 
166         mode_map_ptr = get_mode_map;
167     }
168 
169     /*
170      * NOTE: if you update this chart, please update the versions in
171      *       local/mib2c-conf.d/parent-set.m2i
172      *       agent/mibgroup/helpers/baby_steps.c
173      * while you're at it.
174      */
175     /*
176      ***********************************************************************
177      * Baby Steps Flow Chart (2004.06.05)                                  *
178      *                                                                     *
179      * +--------------+    +================+    U = unconditional path    *
180      * |optional state|    ||required state||    S = path for success      *
181      * +--------------+    +================+    E = path for error        *
182      ***********************************************************************
183      *
184      *                        +--------------+
185      *                        |     pre      |
186      *                        |   request    |
187      *                        +--------------+
188      *                               | U
189      * +-------------+        +==============+
190      * |    row    |f|<-------||  object    ||
191      * |  create   |1|      E ||  lookup    ||
192      * +-------------+        +==============+
193      *     E |   | S                 | S
194      *       |   +------------------>|
195      *       |                +==============+
196      *       |              E ||   check    ||
197      *       |<---------------||   values   ||
198      *       |                +==============+
199      *       |                       | S
200      *       |                +==============+
201      *       |       +<-------||   undo     ||
202      *       |       |      E ||   setup    ||
203      *       |       |        +==============+
204      *       |       |               | S
205      *       |       |        +==============+
206      *       |       |        ||    set     ||-------------------------->+
207      *       |       |        ||   value    || E                         |
208      *       |       |        +==============+                           |
209      *       |       |               | S                                 |
210      *       |       |        +--------------+                           |
211      *       |       |        |    check     |-------------------------->|
212      *       |       |        |  consistency | E                         |
213      *       |       |        +--------------+                           |
214      *       |       |               | S                                 |
215      *       |       |        +==============+         +==============+  |
216      *       |       |        ||   commit   ||-------->||     undo   ||  |
217      *       |       |        ||            || E       ||    commit  ||  |
218      *       |       |        +==============+         +==============+  |
219      *       |       |               | S                     U |<--------+
220      *       |       |        +--------------+         +==============+
221      *       |       |        | irreversible |         ||    undo    ||
222      *       |       |        |    commit    |         ||     set    ||
223      *       |       |        +--------------+         +==============+
224      *       |       |               | U                     U |
225      *       |       +-------------->|<------------------------+
226      *       |                +==============+
227      *       |                ||   undo     ||
228      *       |                ||  cleanup   ||
229      *       |                +==============+
230      *       +---------------------->| U
231      *                               |
232      *                          (err && f1)------------------->+
233      *                               |                         |
234      *                        +--------------+         +--------------+
235      *                        |    post      |<--------|      row     |
236      *                        |   request    |       U |    release   |
237      *                        +--------------+         +--------------+
238      *
239      */
240     /*
241      * save original mode
242      */
243     save_mode = reqinfo->mode;
244     for(i = 0; i < BABY_STEPS_PER_MODE_MAX; ++i ) {
245         /*
246          * break if we run out of baby steps for this mode
247          */
248         if(mode_map_ptr[i] == BABY_STEP_NONE)
249             break;
250 
251         DEBUGMSGTL(("baby_steps", " baby step mode %s\n",
252                     se_find_label_in_slist("babystep_mode",mode_map_ptr[i])));
253 
254         /*
255          * skip modes the handler didn't register for
256          */
257         if (BSTEP_USE_ORIGINAL != mode_map_ptr[i]) {
258             u_int    mode_flag;
259 
260 #ifndef NETSNMP_NO_WRITE_SUPPORT
261             /*
262              * skip undo commit if commit wasn't hit, and
263              * undo_cleanup if undo_setup wasn't hit.
264              */
265             if((MODE_SET_UNDO == save_mode) &&
266                (MODE_BSTEP_UNDO_COMMIT == mode_map_ptr[i]) &&
267                !(BABY_STEP_COMMIT & bs_modes->completed)) {
268                 DEBUGMSGTL(("baby_steps",
269                             "   skipping commit undo (no commit)\n"));
270                 continue;
271             }
272             else if((MODE_SET_FREE == save_mode) &&
273                (MODE_BSTEP_UNDO_CLEANUP == mode_map_ptr[i]) &&
274                !(BABY_STEP_UNDO_SETUP & bs_modes->completed)) {
275                 DEBUGMSGTL(("baby_steps",
276                             "   skipping undo cleanup (no undo setup)\n"));
277                 continue;
278             }
279 #endif /* NETSNMP_NO_WRITE_SUPPORT */
280 
281             reqinfo->mode = mode_map_ptr[i];
282             mode_flag = netsnmp_baby_step_mode2flag( mode_map_ptr[i] );
283             if((mode_flag & bs_modes->registered))
284                 bs_modes->completed |= mode_flag;
285             else {
286                 DEBUGMSGTL(("baby_steps",
287                             "   skipping mode (not registered)\n"));
288                 continue;
289             }
290 
291 
292         }
293         else {
294             reqinfo->mode = save_mode;
295         }
296 
297 #ifdef BABY_STEPS_NEXT_MODE
298         /*
299          * I can't remember why I wanted the next mode in the request,
300          * but it's not used anywhere, so don't use this code. saved,
301          * in case I remember why I thought needed it. - rstory 040911
302          */
303         if((BABY_STEPS_PER_MODE_MAX - 1) == i)
304             reqinfo->next_mode_ok = BABY_STEP_NONE;
305         else {
306             if(BSTEP_USE_ORIGINAL == mode_map_ptr[i+1])
307                 reqinfo->next_mode_ok = save_mode;
308             else
309                 reqinfo->next_mode_ok = mode_map_ptr[i+1];
310         }
311 #endif
312 
313         /*
314          * call handlers for baby step
315          */
316         rc = netsnmp_call_next_handler(handler, reginfo, reqinfo,
317                                        requests);
318 
319         /*
320          * check for error calling handler (unlikely, but...)
321          */
322         if(rc) {
323             DEBUGMSGTL(("baby_steps", "   ERROR:handler error\n"));
324             break;
325         }
326 
327         /*
328          * check for errors in any of the requests for GET-like, reserve1,
329          * reserve2 and action. (there is no recovery from errors
330          * in commit, free or undo.)
331          */
332         if (MODE_IS_GET(save_mode)
333 #ifndef NETSNMP_NO_WRITE_SUPPORT
334             || (save_mode < SNMP_MSG_INTERNAL_SET_COMMIT)
335 #endif /* NETSNMP_NO_WRITE_SUPPORT */
336             ) {
337             rc = netsnmp_check_requests_error(requests);
338             if(rc) {
339                 DEBUGMSGTL(("baby_steps", "   ERROR:request error\n"));
340                 break;
341             }
342         }
343     }
344 
345     /*
346      * restore original mode
347      */
348     reqinfo->mode = save_mode;
349 
350 
351     return rc;
352 }
353 
354 /** initializes the baby_steps helper which then registers a baby_steps
355  *  handler as a run-time injectable handler for configuration file
356  *  use.
357  */
358 netsnmp_feature_child_of(netsnmp_baby_steps_handler_init,netsnmp_unused);
359 #ifndef NETSNMP_FEATURE_REMOVE_NETSNMP_BABY_STEPS_HANDLER_INIT
360 void
netsnmp_baby_steps_handler_init(void)361 netsnmp_baby_steps_handler_init(void)
362 {
363     netsnmp_mib_handler *handler =
364         netsnmp_baby_steps_handler_get(BABY_STEP_ALL);
365     if (NULL == handler) {
366         netsnmp_handler_free(handler);
367         snmp_log(LOG_ERR, "could not create baby steps handler\n");
368         return;
369     }
370 
371     netsnmp_register_handler_by_name("baby_steps", handler);
372 }
373 #endif /* NETSNMP_FEATURE_REMOVE_NETSNMP_BABY_STEPS_HANDLER_INIT */
374 
375 /** @} */
376 
377 /** @defgroup access_multiplexer baby_steps_access_multiplexer: calls individual access methods based on baby_step mode.
378  *  @ingroup baby_steps
379  *  @{
380  */
381 
382 /** returns a baby_steps handler that can be injected into a given
383  *  handler chain.
384  */
385 netsnmp_mib_handler *
netsnmp_baby_steps_access_multiplexer_get(netsnmp_baby_steps_access_methods * am)386 netsnmp_baby_steps_access_multiplexer_get(netsnmp_baby_steps_access_methods *am)
387 {
388     netsnmp_mib_handler *mh;
389 
390     mh = netsnmp_create_handler("baby_steps_mux",
391                                 _baby_steps_access_multiplexer);
392     if(!mh)
393         return NULL;
394 
395     mh->myvoid = am;
396     mh->flags |= MIB_HANDLER_AUTO_NEXT;
397 
398     return mh;
399 }
400 
401 /** @internal Implements the baby_steps handler */
402 static int
_baby_steps_access_multiplexer(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)403 _baby_steps_access_multiplexer(netsnmp_mib_handler *handler,
404                                netsnmp_handler_registration *reginfo,
405                                netsnmp_agent_request_info *reqinfo,
406                                netsnmp_request_info *requests)
407 {
408     void *temp_void;
409     Netsnmp_Node_Handler *method = NULL;
410     netsnmp_baby_steps_access_methods *access_methods;
411     int rc = SNMP_ERR_NOERROR;
412 
413     /** call handlers should enforce these */
414     netsnmp_assert((handler!=NULL) && (reginfo!=NULL) && (reqinfo!=NULL) &&
415                    (requests!=NULL));
416 
417     DEBUGMSGT(("baby_steps_mux", "mode %s\n",
418                se_find_label_in_slist("babystep_mode",reqinfo->mode)));
419 
420     access_methods = (netsnmp_baby_steps_access_methods *)handler->myvoid;
421     if(!access_methods) {
422         snmp_log(LOG_ERR,"baby_steps_access_multiplexer has no methods\n");
423         return SNMPERR_GENERR;
424     }
425 
426     switch(reqinfo->mode) {
427 
428     case MODE_BSTEP_PRE_REQUEST:
429         if( access_methods->pre_request )
430             method = access_methods->pre_request;
431         break;
432 
433     case MODE_BSTEP_OBJECT_LOOKUP:
434         if( access_methods->object_lookup )
435             method = access_methods->object_lookup;
436         break;
437 
438     case SNMP_MSG_GET:
439     case SNMP_MSG_GETNEXT:
440         if( access_methods->get_values )
441             method = access_methods->get_values;
442         break;
443 
444 #ifndef NETSNMP_NO_WRITE_SUPPORT
445     case MODE_BSTEP_CHECK_VALUE:
446         if( access_methods->object_syntax_checks )
447             method = access_methods->object_syntax_checks;
448         break;
449 
450     case MODE_BSTEP_ROW_CREATE:
451         if( access_methods->row_creation )
452             method = access_methods->row_creation;
453         break;
454 
455     case MODE_BSTEP_UNDO_SETUP:
456         if( access_methods->undo_setup )
457             method = access_methods->undo_setup;
458         break;
459 
460     case MODE_BSTEP_SET_VALUE:
461         if( access_methods->set_values )
462             method = access_methods->set_values;
463         break;
464 
465     case MODE_BSTEP_CHECK_CONSISTENCY:
466         if( access_methods->consistency_checks )
467             method = access_methods->consistency_checks;
468         break;
469 
470     case MODE_BSTEP_UNDO_SET:
471         if( access_methods->undo_sets )
472             method = access_methods->undo_sets;
473         break;
474 
475     case MODE_BSTEP_COMMIT:
476         if( access_methods->commit )
477             method = access_methods->commit;
478         break;
479 
480     case MODE_BSTEP_UNDO_COMMIT:
481         if( access_methods->undo_commit )
482             method = access_methods->undo_commit;
483         break;
484 
485     case MODE_BSTEP_IRREVERSIBLE_COMMIT:
486         if( access_methods->irreversible_commit )
487             method = access_methods->irreversible_commit;
488         break;
489 
490     case MODE_BSTEP_UNDO_CLEANUP:
491         if( access_methods->undo_cleanup )
492             method = access_methods->undo_cleanup;
493         break;
494 #endif /* NETSNMP_NO_WRITE_SUPPORT */
495 
496     case MODE_BSTEP_POST_REQUEST:
497         if( access_methods->post_request )
498             method = access_methods->post_request;
499         break;
500 
501     default:
502         snmp_log(LOG_ERR,"unknown mode %d\n", reqinfo->mode);
503         return SNMP_ERR_GENERR;
504     }
505 
506     /*
507      * if method exists, set up handler void and call method.
508      */
509     if(NULL != method) {
510         temp_void = handler->myvoid;
511         handler->myvoid = access_methods->my_access_void;
512         rc = (*method)(handler, reginfo, reqinfo, requests);
513         handler->myvoid = temp_void;
514     }
515     else {
516         rc = SNMP_ERR_GENERR;
517         snmp_log(LOG_ERR,"baby steps multiplexer handler called for a mode "
518                  "with no handler\n");
519         netsnmp_assert(NULL != method);
520     }
521 
522     /*
523      * don't call any lower handlers, it will be done for us
524      * since we set MIB_HANDLER_AUTO_NEXT
525      */
526 
527     return rc;
528 }
529 
530 /*
531  * give a baby step mode, return the flag for that mode
532  */
533 int
netsnmp_baby_step_mode2flag(u_int mode)534 netsnmp_baby_step_mode2flag( u_int mode )
535 {
536     switch( mode ) {
537         case MODE_BSTEP_OBJECT_LOOKUP:
538             return BABY_STEP_OBJECT_LOOKUP;
539 #ifndef NETSNMP_NO_WRITE_SUPPORT
540         case MODE_BSTEP_SET_VALUE:
541             return BABY_STEP_SET_VALUE;
542         case MODE_BSTEP_IRREVERSIBLE_COMMIT:
543             return BABY_STEP_IRREVERSIBLE_COMMIT;
544         case MODE_BSTEP_CHECK_VALUE:
545             return BABY_STEP_CHECK_VALUE;
546         case MODE_BSTEP_PRE_REQUEST:
547             return BABY_STEP_PRE_REQUEST;
548         case MODE_BSTEP_POST_REQUEST:
549             return BABY_STEP_POST_REQUEST;
550         case MODE_BSTEP_UNDO_SETUP:
551             return BABY_STEP_UNDO_SETUP;
552         case MODE_BSTEP_UNDO_CLEANUP:
553             return BABY_STEP_UNDO_CLEANUP;
554         case MODE_BSTEP_UNDO_SET:
555             return BABY_STEP_UNDO_SET;
556         case MODE_BSTEP_ROW_CREATE:
557             return BABY_STEP_ROW_CREATE;
558         case MODE_BSTEP_CHECK_CONSISTENCY:
559             return BABY_STEP_CHECK_CONSISTENCY;
560         case MODE_BSTEP_COMMIT:
561             return BABY_STEP_COMMIT;
562         case MODE_BSTEP_UNDO_COMMIT:
563             return BABY_STEP_UNDO_COMMIT;
564 #endif /* NETSNMP_NO_WRITE_SUPPORT */
565         default:
566             netsnmp_assert("unknown flag");
567             break;
568     }
569     return 0;
570 }
571 /**  @} */
572 
573 #else  /* NETSNMP_FEATURE_REMOVE_BABY_STEPS */
574 netsnmp_feature_unused(baby_steps);
575 #endif /* NETSNMP_FEATURE_REMOVE_BABY_STEPS */
576 
577