1 
2 #include <net-snmp/net-snmp-config.h>
3 #include <net-snmp/net-snmp-features.h>
4 #include <net-snmp/net-snmp-includes.h>
5 #include <net-snmp/agent/net-snmp-agent-includes.h>
6 #include <net-snmp/agent/watcher.h>
7 #include <net-snmp/agent/agent_callbacks.h>
8 
9 #include "agent/extend.h"
10 #include "utilities/execute.h"
11 #include "struct.h"
12 
13 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
14 #include "util_funcs/header_simple_table.h"
15 #include "mibdefs.h"
16 #define SHELLCOMMAND 3
17 #endif
18 
19 /*  This mib is potentially dangerous to turn on by default, since it
20  *  allows arbitrary commands to be set by anyone with SNMP WRITE
21  *  access to the MIB table.  If all of your users are "root" level
22  *  users, then it may be safe to turn on. */
23 #define ENABLE_EXTEND_WRITE_ACCESS 0
24 
25 netsnmp_feature_require(extract_table_row_data);
26 netsnmp_feature_require(table_data_delete_table);
27 #ifndef NETSNMP_NO_WRITE_SUPPORT
28 netsnmp_feature_require(insert_table_row);
29 #endif /* NETSNMP_NO_WRITE_SUPPORT */
30 
31 oid  ns_extend_oid[]    = { 1, 3, 6, 1, 4, 1, 8072, 1, 3, 2 };
32 
33 typedef struct extend_registration_block_s {
34     netsnmp_table_data *dinfo;
35     oid                *root_oid;
36     size_t              oid_len;
37     long                num_entries;
38     netsnmp_extend     *ehead;
39     netsnmp_handler_registration       *reg[4];
40     struct extend_registration_block_s *next;
41 } extend_registration_block;
42 extend_registration_block *ereg_head = NULL;
43 
44 
45 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
46 typedef struct netsnmp_old_extend_s {
47     unsigned int idx;
48     netsnmp_extend *exec_entry;
49     netsnmp_extend *efix_entry;
50 } netsnmp_old_extend;
51 
52 unsigned int             num_compatability_entries = 0;
53 unsigned int             max_compatability_entries = 50;
54 netsnmp_old_extend *compatability_entries;
55 
56 char           *cmdlinebuf;
57 size_t          cmdlinesize;
58 
59 WriteMethod fixExec2Error;
60 FindVarMethod var_extensible_old;
61 oid  old_extensible_variables_oid[] = { NETSNMP_UCDAVIS_MIB, NETSNMP_SHELLMIBNUM, 1 };
62 #ifndef NETSNMP_NO_WRITE_SUPPORT
63 struct variable2 old_extensible_variables[] = {
64     {MIBINDEX,     ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
65      var_extensible_old, 1, {MIBINDEX}},
66     {ERRORNAME,    ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
67      var_extensible_old, 1, {ERRORNAME}},
68     {SHELLCOMMAND, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
69      var_extensible_old, 1, {SHELLCOMMAND}},
70     {ERRORFLAG,    ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
71      var_extensible_old, 1, {ERRORFLAG}},
72     {ERRORMSG,     ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
73      var_extensible_old, 1, {ERRORMSG}},
74     {ERRORFIX,     ASN_INTEGER,  NETSNMP_OLDAPI_RWRITE,
75      var_extensible_old, 1, {ERRORFIX}},
76     {ERRORFIXCMD,  ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
77      var_extensible_old, 1, {ERRORFIXCMD}}
78 };
79 #else /* !NETSNMP_NO_WRITE_SUPPORT */
80 struct variable2 old_extensible_variables[] = {
81     {MIBINDEX,     ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
82      var_extensible_old, 1, {MIBINDEX}},
83     {ERRORNAME,    ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
84      var_extensible_old, 1, {ERRORNAME}},
85     {SHELLCOMMAND, ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
86      var_extensible_old, 1, {SHELLCOMMAND}},
87     {ERRORFLAG,    ASN_INTEGER,   NETSNMP_OLDAPI_RONLY,
88      var_extensible_old, 1, {ERRORFLAG}},
89     {ERRORMSG,     ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
90      var_extensible_old, 1, {ERRORMSG}},
91     {ERRORFIX,     ASN_INTEGER,  NETSNMP_OLDAPI_RONLY,
92      var_extensible_old, 1, {ERRORFIX}},
93     {ERRORFIXCMD,  ASN_OCTET_STR, NETSNMP_OLDAPI_RONLY,
94      var_extensible_old, 1, {ERRORFIXCMD}}
95 };
96 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
97 #endif
98 
99 
100         /*************************
101          *
102          *  Main initialisation routine
103          *
104          *************************/
105 
106 extend_registration_block *
_find_extension_block(oid * name,size_t name_len)107 _find_extension_block( oid *name, size_t name_len )
108 {
109     extend_registration_block         *eptr;
110     size_t len;
111     for ( eptr=ereg_head; eptr; eptr=eptr->next ) {
112         len = SNMP_MIN(name_len, eptr->oid_len);
113         if (!snmp_oid_compare( name, len, eptr->root_oid, eptr->oid_len))
114             return eptr;
115     }
116     return NULL;
117 }
118 
119 extend_registration_block *
_register_extend(oid * base,size_t len)120 _register_extend( oid *base, size_t len )
121 {
122     extend_registration_block         *eptr;
123     oid oid_buf[MAX_OID_LEN];
124 
125     netsnmp_table_data                *dinfo;
126     netsnmp_table_registration_info   *tinfo;
127     netsnmp_watcher_info              *winfo;
128     netsnmp_handler_registration      *reg = NULL;
129     int                                rc;
130 
131     for ( eptr=ereg_head; eptr; eptr=eptr->next ) {
132         if (!snmp_oid_compare( base, len, eptr->root_oid, eptr->oid_len))
133             return eptr;
134     }
135     if (!eptr) {
136         eptr = SNMP_MALLOC_TYPEDEF( extend_registration_block );
137         if (!eptr)
138             return NULL;
139         eptr->root_oid = snmp_duplicate_objid( base, len );
140         eptr->oid_len  = len;
141         eptr->num_entries = 0;
142         eptr->ehead       = NULL;
143         eptr->dinfo       = netsnmp_create_table_data( "nsExtendTable" );
144         eptr->next        = ereg_head;
145         ereg_head         = eptr;
146     }
147 
148     dinfo = eptr->dinfo;
149     memcpy( oid_buf, base, len*sizeof(oid) );
150 
151         /*
152          * Register the configuration table
153          */
154     tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
155     netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, 0 );
156     tinfo->min_column = COLUMN_EXTCFG_FIRST_COLUMN;
157     tinfo->max_column = COLUMN_EXTCFG_LAST_COLUMN;
158     oid_buf[len] = 2;
159 #ifndef NETSNMP_NO_WRITE_SUPPORT
160     reg   = netsnmp_create_handler_registration(
161                 "nsExtendConfigTable", handle_nsExtendConfigTable,
162                 oid_buf, len+1, HANDLER_CAN_RWRITE);
163 #else /* !NETSNMP_NO_WRITE_SUPPORT */
164     reg   = netsnmp_create_handler_registration(
165                 "nsExtendConfigTable", handle_nsExtendConfigTable,
166                 oid_buf, len+1, HANDLER_CAN_RONLY);
167 #endif /* !NETSNMP_NO_WRITE_SUPPORT */
168     rc = netsnmp_register_table_data( reg, dinfo, tinfo );
169     if (rc != SNMPERR_SUCCESS) {
170         goto bail;
171     }
172     netsnmp_handler_owns_table_info(reg->handler->next);
173     eptr->reg[0] = reg;
174 
175         /*
176          * Register the main output table
177          *   using the same table_data handle.
178          * This is sufficient to link the two tables,
179          *   and implement the AUGMENTS behaviour
180          */
181     tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
182     netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, 0 );
183     tinfo->min_column = COLUMN_EXTOUT1_FIRST_COLUMN;
184     tinfo->max_column = COLUMN_EXTOUT1_LAST_COLUMN;
185     oid_buf[len] = 3;
186     reg   = netsnmp_create_handler_registration(
187                 "nsExtendOut1Table", handle_nsExtendOutput1Table,
188                 oid_buf, len+1, HANDLER_CAN_RONLY);
189     rc = netsnmp_register_table_data( reg, dinfo, tinfo );
190     if (rc != SNMPERR_SUCCESS)
191         goto bail;
192     netsnmp_handler_owns_table_info(reg->handler->next);
193     eptr->reg[1] = reg;
194 
195         /*
196          * Register the multi-line output table
197          *   using a simple table helper.
198          * This handles extracting the indexes from
199          *   the request OID, but leaves most of
200          *   the work to our handler routine.
201          * Still, it was nice while it lasted...
202          */
203     tinfo = SNMP_MALLOC_TYPEDEF( netsnmp_table_registration_info );
204     netsnmp_table_helper_add_indexes( tinfo, ASN_OCTET_STR, ASN_INTEGER, 0 );
205     tinfo->min_column = COLUMN_EXTOUT2_FIRST_COLUMN;
206     tinfo->max_column = COLUMN_EXTOUT2_LAST_COLUMN;
207     oid_buf[len] = 4;
208     reg   = netsnmp_create_handler_registration(
209                 "nsExtendOut2Table", handle_nsExtendOutput2Table,
210                 oid_buf, len+1, HANDLER_CAN_RONLY);
211     rc = netsnmp_register_table( reg, tinfo );
212     if (rc != SNMPERR_SUCCESS)
213         goto bail;
214     netsnmp_handler_owns_table_info(reg->handler->next);
215     eptr->reg[2] = reg;
216 
217         /*
218          * Register a watched scalar to keep track of the number of entries
219          */
220     oid_buf[len] = 1;
221     reg   = netsnmp_create_handler_registration(
222                 "nsExtendNumEntries", NULL,
223                 oid_buf, len+1, HANDLER_CAN_RONLY);
224     winfo = netsnmp_create_watcher_info(
225                 &(eptr->num_entries), sizeof(eptr->num_entries),
226                 ASN_INTEGER, WATCHER_FIXED_SIZE);
227     rc = netsnmp_register_watched_scalar2( reg, winfo );
228     if (rc != SNMPERR_SUCCESS)
229         goto bail;
230     eptr->reg[3] = reg;
231 
232     return eptr;
233 
234 bail:
235     if (eptr->reg[3])
236         netsnmp_unregister_handler(eptr->reg[3]);
237     if (eptr->reg[2])
238         netsnmp_unregister_handler(eptr->reg[2]);
239     if (eptr->reg[1])
240         netsnmp_unregister_handler(eptr->reg[1]);
241     if (eptr->reg[0])
242         netsnmp_unregister_handler(eptr->reg[0]);
243     return NULL;
244 }
245 
246 static void
_unregister_extend(extend_registration_block * eptr)247 _unregister_extend(extend_registration_block *eptr)
248 {
249     extend_registration_block *prev;
250 
251     netsnmp_assert(eptr);
252     for (prev = ereg_head; prev && prev->next != eptr; prev = prev->next)
253 	;
254     if (prev) {
255         netsnmp_assert(eptr == prev->next);
256 	prev->next = eptr->next;
257     } else {
258         netsnmp_assert(eptr == ereg_head);
259 	ereg_head = eptr->next;
260     }
261 
262     netsnmp_table_data_delete_table(eptr->dinfo);
263     free(eptr->root_oid);
264     free(eptr);
265 }
266 
267 int
extend_clear_callback(int majorID,int minorID,void * serverarg,void * clientarg)268 extend_clear_callback(int majorID, int minorID,
269                     void *serverarg, void *clientarg)
270 {
271     extend_registration_block *eptr, *enext = NULL;
272 
273     for ( eptr=ereg_head; eptr; eptr=enext ) {
274         enext=eptr->next;
275         netsnmp_unregister_handler( eptr->reg[0] );
276         netsnmp_unregister_handler( eptr->reg[1] );
277         netsnmp_unregister_handler( eptr->reg[2] );
278         netsnmp_unregister_handler( eptr->reg[3] );
279         SNMP_FREE(eptr);
280     }
281     ereg_head = NULL;
282     return 0;
283 }
284 
init_extend(void)285 void init_extend( void )
286 {
287     snmpd_register_config_handler("extend",    extend_parse_config, NULL, NULL);
288     snmpd_register_config_handler("extend-sh", extend_parse_config, NULL, NULL);
289     snmpd_register_config_handler("extendfix", extend_parse_config, NULL, NULL);
290     snmpd_register_config_handler("exec2", extend_parse_config, NULL, NULL);
291     snmpd_register_config_handler("sh2",   extend_parse_config, NULL, NULL);
292     snmpd_register_config_handler("execFix2", extend_parse_config, NULL, NULL);
293     (void)_register_extend( ns_extend_oid, OID_LENGTH(ns_extend_oid));
294 
295 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
296     snmpd_register_config_handler("exec", extend_parse_config, NULL, NULL);
297     snmpd_register_config_handler("sh",   extend_parse_config, NULL, NULL);
298     snmpd_register_config_handler("execFix", extend_parse_config, NULL, NULL);
299     compatability_entries = (netsnmp_old_extend *)
300         calloc( max_compatability_entries, sizeof(netsnmp_old_extend));
301     REGISTER_MIB("ucd-extensible", old_extensible_variables,
302                  variable2, old_extensible_variables_oid);
303 #endif
304 
305     snmp_register_callback(SNMP_CALLBACK_APPLICATION,
306                            SNMPD_CALLBACK_PRE_UPDATE_CONFIG,
307                            extend_clear_callback, NULL);
308 }
309 
310 void
shutdown_extend(void)311 shutdown_extend(void)
312 {
313 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
314     free(compatability_entries);
315     compatability_entries = NULL;
316 #endif
317     while (ereg_head)
318 	_unregister_extend(ereg_head);
319 }
320 
321         /*************************
322          *
323          *  Cached-data hooks
324          *  see 'cache_handler' helper
325          *
326          *************************/
327 
328 int
extend_load_cache(netsnmp_cache * cache,void * magic)329 extend_load_cache(netsnmp_cache *cache, void *magic)
330 {
331 #ifndef USING_UTILITIES_EXECUTE_MODULE
332     NETSNMP_LOGONCE((LOG_WARNING,"support for run_exec_command not available\n"));
333     return -1;
334 #else
335     int  out_len = 1024*100;
336     char out_buf[ 1024*100 ];
337     int  cmd_len = 255*2 + 2;	/* 2 * DisplayStrings */
338     char cmd_buf[ 255*2 + 2 ];
339     int  ret;
340     char *cp;
341     char *line_buf[ 1024 ];
342     netsnmp_extend *extension = (netsnmp_extend *)magic;
343 
344     if (!magic)
345         return -1;
346     DEBUGMSGTL(( "nsExtendTable:cache", "load %s", extension->token ));
347     if ( extension->args )
348         snprintf( cmd_buf, cmd_len, "%s %s", extension->command, extension->args );
349     else
350         snprintf( cmd_buf, cmd_len, "%s", extension->command );
351     if ( extension->flags & NS_EXTEND_FLAGS_SHELL )
352         ret = run_shell_command( cmd_buf, extension->input, out_buf, &out_len);
353     else
354         ret = run_exec_command(  cmd_buf, extension->input, out_buf, &out_len);
355     DEBUGMSG(( "nsExtendTable:cache", ": %s : %d\n", cmd_buf, ret));
356     if (ret >= 0) {
357         if (out_buf[   out_len-1 ] == '\n')
358             out_buf[ --out_len   ] =  '\0';	/* Stomp on trailing newline */
359         extension->output   = strdup( out_buf );
360         extension->out_len  = out_len;
361         /*
362          * Now we need to pick the output apart into separate lines.
363          * Start by counting how many lines we've got, and keeping
364          * track of where each line starts in a static buffer
365          */
366         extension->numlines = 1;
367         line_buf[ 0 ] = extension->output;
368         for (cp=extension->output; *cp; cp++) {
369             if (*cp == '\n') {
370                 line_buf[ extension->numlines++ ] = cp+1;
371             }
372         }
373         if ( extension->numlines > 1 ) {
374             extension->lines = (char**)calloc( sizeof(char *), extension->numlines );
375             memcpy( extension->lines, line_buf,
376                                        sizeof(char *) * extension->numlines );
377         } else {
378             extension->lines = &extension->output;
379         }
380     }
381     extension->result = ret;
382     return ret;
383 #endif /* !defined(USING_UTILITIES_EXECUTE_MODULE) */
384 }
385 
386 void
extend_free_cache(netsnmp_cache * cache,void * magic)387 extend_free_cache(netsnmp_cache *cache, void *magic)
388 {
389     netsnmp_extend *extension = (netsnmp_extend *)magic;
390     if (!magic)
391         return;
392 
393     DEBUGMSGTL(( "nsExtendTable:cache", "free %s\n", extension->token ));
394     if (extension->output) {
395         SNMP_FREE(extension->output);
396         extension->output = NULL;
397     }
398     if ( extension->numlines > 1 ) {
399         SNMP_FREE(extension->lines);
400     }
401     extension->lines  = NULL;
402     extension->out_len  = 0;
403     extension->numlines = 0;
404 }
405 
406 
407         /*************************
408          *
409          *  Utility routines for setting up a new entry
410          *  (either via SET requests, or the config file)
411          *
412          *************************/
413 
414 void
_free_extension(netsnmp_extend * extension,extend_registration_block * ereg)415 _free_extension( netsnmp_extend *extension, extend_registration_block *ereg )
416 {
417     netsnmp_extend *eptr  = NULL;
418     netsnmp_extend *eprev = NULL;
419 
420     if (!extension)
421         return;
422 
423     if (ereg) {
424         /* Unlink from 'ehead' list */
425         for (eptr=ereg->ehead; eptr; eptr=eptr->next) {
426             if (eptr == extension)
427                 break;
428             eprev = eptr;
429         }
430         if (!eptr) {
431             snmp_log(LOG_ERR,
432                      "extend: fell off end of list before finding extension\n");
433             return;
434         }
435         if (eprev)
436             eprev->next = eptr->next;
437         else
438             ereg->ehead = eptr->next;
439         netsnmp_table_data_remove_and_delete_row( ereg->dinfo, extension->row);
440     }
441 
442     SNMP_FREE( extension->token );
443     SNMP_FREE( extension->cache );
444     SNMP_FREE( extension->command );
445     SNMP_FREE( extension->args  );
446     SNMP_FREE( extension->input );
447     SNMP_FREE( extension );
448     return;
449 }
450 
451 netsnmp_extend *
_new_extension(char * exec_name,int exec_flags,extend_registration_block * ereg)452 _new_extension( char *exec_name, int exec_flags, extend_registration_block *ereg )
453 {
454     netsnmp_extend     *extension;
455     netsnmp_table_row  *row;
456     netsnmp_extend     *eptr1, *eptr2;
457     netsnmp_table_data *dinfo = ereg->dinfo;
458 
459     if (!exec_name)
460         return NULL;
461     extension = SNMP_MALLOC_TYPEDEF( netsnmp_extend );
462     if (!extension)
463         return NULL;
464     extension->token    = strdup( exec_name );
465     extension->flags    = exec_flags;
466     extension->cache    = netsnmp_cache_create( 0, extend_load_cache,
467                                                    extend_free_cache, NULL, 0 );
468     if (extension->cache)
469         extension->cache->magic = extension;
470 
471     row = netsnmp_create_table_data_row();
472     if (!row || !extension->cache) {
473         _free_extension( extension, ereg );
474         SNMP_FREE( row );
475         return NULL;
476     }
477     row->data = (void *)extension;
478     extension->row = row;
479     netsnmp_table_row_add_index( row, ASN_OCTET_STR,
480                                  exec_name, strlen(exec_name));
481     if ( netsnmp_table_data_add_row( dinfo, row) != SNMPERR_SUCCESS ) {
482         /* _free_extension( extension, ereg ); */
483         SNMP_FREE( extension );  /* Probably not sufficient */
484         SNMP_FREE( row );
485         return NULL;
486     }
487 
488     ereg->num_entries++;
489         /*
490          *  Now add this structure to a private linked list.
491          *  We don't need this for the main tables - the
492          *   'table_data' helper will take care of those.
493          *  But it's probably easier to handle the multi-line
494          *  output table ourselves, for which we need access
495          *  to the underlying data.
496          *   So we'll keep a list internally as well.
497          */
498     for ( eptr1 = ereg->ehead, eptr2 = NULL;
499           eptr1;
500           eptr2 = eptr1, eptr1 = eptr1->next ) {
501 
502         if (strlen( eptr1->token )  > strlen( exec_name ))
503             break;
504         if (strlen( eptr1->token ) == strlen( exec_name ) &&
505             strcmp( eptr1->token, exec_name ) > 0 )
506             break;
507     }
508     if ( eptr2 )
509         eptr2->next = extension;
510     else
511         ereg->ehead = extension;
512     extension->next = eptr1;
513     return extension;
514 }
515 
516 void
extend_parse_config(const char * token,char * cptr)517 extend_parse_config(const char *token, char *cptr)
518 {
519     netsnmp_extend *extension;
520     char exec_name[STRMAX];
521     char exec_name2[STRMAX];     /* For use with UCD execFix directive */
522     char exec_command[STRMAX];
523     oid  oid_buf[MAX_OID_LEN];
524     size_t oid_len;
525     extend_registration_block *eptr;
526     int  flags;
527     int cache_timeout = 0;
528     int exec_type = NS_EXTEND_ETYPE_EXEC;
529 
530     cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
531     if (strcmp(exec_name, "-cacheTime") == 0) {
532         char cache_timeout_str[32];
533 
534         cptr = copy_nword(cptr, cache_timeout_str, sizeof(cache_timeout_str));
535         /* If atoi can't do the conversion, it returns 0 */
536         cache_timeout = atoi(cache_timeout_str);
537         cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
538     }
539     if (strcmp(exec_name, "-execType") == 0) {
540         char exec_type_str[16];
541 
542         cptr = copy_nword(cptr, exec_type_str, sizeof(exec_type_str));
543         if (strcmp(exec_type_str, "sh") == 0)
544             exec_type = NS_EXTEND_ETYPE_SHELL;
545         else
546             exec_type = NS_EXTEND_ETYPE_EXEC;
547         cptr = copy_nword(cptr, exec_name, sizeof(exec_name));
548     }
549     if ( *exec_name == '.' ) {
550         oid_len = MAX_OID_LEN - 2;
551         if (0 == read_objid( exec_name, oid_buf, &oid_len )) {
552             config_perror("ERROR: Unrecognised OID" );
553             return;
554         }
555         cptr = copy_nword(cptr, exec_name,    sizeof(exec_name));
556         if (!strcmp( token, "sh"   ) ||
557             !strcmp( token, "exec" )) {
558             config_perror("ERROR: This output format has been deprecated - Please use the 'extend' directive instead" );
559             return;
560         }
561     } else {
562         memcpy( oid_buf, ns_extend_oid, sizeof(ns_extend_oid));
563         oid_len = OID_LENGTH(ns_extend_oid);
564     }
565     cptr = copy_nword(cptr, exec_command, sizeof(exec_command));
566     /* XXX - check 'exec_command' exists & is executable */
567     flags = (NS_EXTEND_FLAGS_ACTIVE | NS_EXTEND_FLAGS_CONFIG);
568     if (!strcmp( token, "sh"        ) ||
569         !strcmp( token, "extend-sh" ) ||
570         !strcmp( token, "sh2") ||
571         exec_type == NS_EXTEND_ETYPE_SHELL)
572         flags |= NS_EXTEND_FLAGS_SHELL;
573     if (!strcmp( token, "execFix"   ) ||
574         !strcmp( token, "extendfix" ) ||
575         !strcmp( token, "execFix2" )) {
576         strcpy( exec_name2, exec_name );
577         strcat( exec_name, "Fix" );
578         flags |= NS_EXTEND_FLAGS_WRITEABLE;
579         /* XXX - Check for shell... */
580     }
581 
582     eptr      = _register_extend( oid_buf, oid_len );
583     if (!eptr) {
584         snmp_log(LOG_ERR, "Failed to register extend entry '%s' - possibly duplicate name.\n", exec_name );
585         return;
586     }
587     extension = _new_extension( exec_name, flags, eptr );
588     if (extension) {
589         extension->command  = strdup( exec_command );
590         if (cptr)
591             extension->args = strdup( cptr );
592         if (cache_timeout != 0)
593             extension->cache->timeout = cache_timeout;
594     } else {
595         snmp_log(LOG_ERR, "Failed to register extend entry '%s' - possibly duplicate name.\n", exec_name );
596         return;
597     }
598 
599 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
600     /*
601      *  Compatability with the UCD extTable
602      */
603     if (!strcmp( token, "execFix"  )) {
604         int  i;
605         for ( i=0; i < num_compatability_entries; i++ ) {
606             if (!strcmp( exec_name2,
607                     compatability_entries[i].exec_entry->token))
608                 break;
609         }
610         if ( i == num_compatability_entries )
611             config_perror("No matching exec entry" );
612         else
613             compatability_entries[ i ].efix_entry = extension;
614 
615     } else if (!strcmp( token, "sh"   ) ||
616                !strcmp( token, "exec" )) {
617         if ( num_compatability_entries == max_compatability_entries ) {
618             /* XXX - should really use dynamic allocation */
619             netsnmp_old_extend *new_compatability_entries;
620             new_compatability_entries = realloc(compatability_entries,
621                              max_compatability_entries*2*sizeof(netsnmp_old_extend));
622             if (!new_compatability_entries)
623                 config_perror("No further UCD-compatible entries" );
624             else {
625                 memset(new_compatability_entries+num_compatability_entries, 0,
626                         sizeof(netsnmp_old_extend)*max_compatability_entries);
627                 max_compatability_entries *= 2;
628                 compatability_entries = new_compatability_entries;
629             }
630         }
631         if (num_compatability_entries != max_compatability_entries)
632             compatability_entries[
633                 num_compatability_entries++ ].exec_entry = extension;
634     }
635 #endif
636 }
637 
638         /*************************
639          *
640          *  Main table handlers
641          *  Most of the work is handled
642          *   by the 'table_data' helper.
643          *
644          *************************/
645 
646 int
handle_nsExtendConfigTable(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)647 handle_nsExtendConfigTable(netsnmp_mib_handler          *handler,
648                      netsnmp_handler_registration *reginfo,
649                      netsnmp_agent_request_info   *reqinfo,
650                      netsnmp_request_info         *requests)
651 {
652     netsnmp_request_info       *request;
653     netsnmp_table_request_info *table_info;
654     netsnmp_extend             *extension;
655     extend_registration_block  *eptr NETSNMP_ATTRIBUTE_UNUSED;
656     int  i;
657     int  need_to_validate NETSNMP_ATTRIBUTE_UNUSED = 0;
658 
659     for ( request=requests; request; request=request->next ) {
660         if (request->processed)
661             continue;
662         table_info = netsnmp_extract_table_info( request );
663         extension  = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
664 
665         DEBUGMSGTL(( "nsExtendTable:config", "varbind: "));
666         DEBUGMSGOID(("nsExtendTable:config", request->requestvb->name,
667                                              request->requestvb->name_length));
668         DEBUGMSG((   "nsExtendTable:config", " (%s)\n",
669                       se_find_label_in_slist("agent_mode", reqinfo->mode)));
670 
671         switch (reqinfo->mode) {
672         case MODE_GET:
673             switch (table_info->colnum) {
674             case COLUMN_EXTCFG_COMMAND:
675                 snmp_set_var_typed_value(
676                      request->requestvb, ASN_OCTET_STR,
677                      extension->command,
678                     (extension->command)?strlen(extension->command):0);
679                 break;
680             case COLUMN_EXTCFG_ARGS:
681                 snmp_set_var_typed_value(
682                      request->requestvb, ASN_OCTET_STR,
683                      extension->args,
684                     (extension->args)?strlen(extension->args):0);
685                 break;
686             case COLUMN_EXTCFG_INPUT:
687                 snmp_set_var_typed_value(
688                      request->requestvb, ASN_OCTET_STR,
689                      extension->input,
690                     (extension->input)?strlen(extension->input):0);
691                 break;
692             case COLUMN_EXTCFG_CACHETIME:
693                 snmp_set_var_typed_value(
694                      request->requestvb, ASN_INTEGER,
695                     (u_char*)&extension->cache->timeout, sizeof(int));
696                 break;
697             case COLUMN_EXTCFG_EXECTYPE:
698                 i = ((extension->flags & NS_EXTEND_FLAGS_SHELL) ?
699                                          NS_EXTEND_ETYPE_SHELL :
700                                          NS_EXTEND_ETYPE_EXEC);
701                 snmp_set_var_typed_value(
702                      request->requestvb, ASN_INTEGER,
703                     (u_char*)&i, sizeof(i));
704                 break;
705             case COLUMN_EXTCFG_RUNTYPE:
706                 i = ((extension->flags & NS_EXTEND_FLAGS_WRITEABLE) ?
707                                          NS_EXTEND_RTYPE_RWRITE :
708                                          NS_EXTEND_RTYPE_RONLY);
709                 snmp_set_var_typed_value(
710                      request->requestvb, ASN_INTEGER,
711                     (u_char*)&i, sizeof(i));
712                 break;
713 
714             case COLUMN_EXTCFG_STORAGE:
715                 i = ((extension->flags & NS_EXTEND_FLAGS_CONFIG) ?
716                                          ST_PERMANENT : ST_VOLATILE);
717                 snmp_set_var_typed_value(
718                      request->requestvb, ASN_INTEGER,
719                     (u_char*)&i, sizeof(i));
720                 break;
721             case COLUMN_EXTCFG_STATUS:
722                 i = ((extension->flags & NS_EXTEND_FLAGS_ACTIVE) ?
723                                          RS_ACTIVE :
724                                          RS_NOTINSERVICE);
725                 snmp_set_var_typed_value(
726                      request->requestvb, ASN_INTEGER,
727                     (u_char*)&i, sizeof(i));
728                 break;
729 
730             default:
731                 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
732                 continue;
733             }
734             break;
735 
736         /**********
737          *
738          * Start of SET handling
739          *
740          *   All config objects are potentially writable except
741          *     nsExtendStorage which is fixed as either 'permanent'
742          *     (if read from a config file) or 'volatile' (if set via SNMP)
743          *   The string-based settings of a 'permanent' entry cannot
744          *     be changed - neither can the execution or run type.
745          *   Such entries can be (temporarily) marked as inactive,
746          *     and the cache timeout adjusted, but these changes are
747          *     not persistent.
748          *
749          **********/
750 
751 #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS
752         case MODE_SET_RESERVE1:
753             /*
754              * Validate the new assignments
755              */
756             switch (table_info->colnum) {
757             case COLUMN_EXTCFG_COMMAND:
758                 if (request->requestvb->type != ASN_OCTET_STR) {
759                     netsnmp_set_request_error(reqinfo, request,
760                                               SNMP_ERR_WRONGTYPE);
761                     return SNMP_ERR_WRONGTYPE;
762                 }
763                 /*
764                  * Must have a full path to the command
765                  * XXX - Assumes Unix-style paths
766                  */
767                 if (request->requestvb->val_len == 0 ||
768                     request->requestvb->val.string[0] != '/') {
769                     netsnmp_set_request_error(reqinfo, request,
770                                               SNMP_ERR_WRONGVALUE);
771                     return SNMP_ERR_WRONGVALUE;
772                 }
773                 /*
774                  * XXX - need to check this file exists
775                  *       (and is executable)
776                  */
777 
778                 if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
779                     /*
780                      * config entries are "permanent" so can't be changed
781                      */
782                     netsnmp_set_request_error(reqinfo, request,
783                                               SNMP_ERR_NOTWRITABLE);
784                     return SNMP_ERR_NOTWRITABLE;
785                 }
786                 break;
787 
788             case COLUMN_EXTCFG_ARGS:
789             case COLUMN_EXTCFG_INPUT:
790                 if (request->requestvb->type != ASN_OCTET_STR) {
791                     netsnmp_set_request_error(reqinfo, request,
792                                               SNMP_ERR_WRONGTYPE);
793                     return SNMP_ERR_WRONGTYPE;
794                 }
795 
796                 if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
797                     /*
798                      * config entries are "permanent" so can't be changed
799                      */
800                     netsnmp_set_request_error(reqinfo, request,
801                                               SNMP_ERR_NOTWRITABLE);
802                     return SNMP_ERR_NOTWRITABLE;
803                 }
804                 break;
805 
806             case COLUMN_EXTCFG_CACHETIME:
807                 if (request->requestvb->type != ASN_INTEGER) {
808                     netsnmp_set_request_error(reqinfo, request,
809                                               SNMP_ERR_WRONGTYPE);
810                     return SNMP_ERR_WRONGTYPE;
811                 }
812                 i = *request->requestvb->val.integer;
813                 /*
814                  * -1 is a special value indicating "don't cache"
815                  *    [[ XXX - should this be 0 ?? ]]
816                  * Otherwise, cache times must be non-negative
817                  */
818                 if (i < -1 ) {
819                     netsnmp_set_request_error(reqinfo, request,
820                                               SNMP_ERR_WRONGVALUE);
821                     return SNMP_ERR_WRONGVALUE;
822                 }
823                 break;
824 
825             case COLUMN_EXTCFG_EXECTYPE:
826                 if (request->requestvb->type != ASN_INTEGER) {
827                     netsnmp_set_request_error(reqinfo, request,
828                                               SNMP_ERR_WRONGTYPE);
829                     return SNMP_ERR_WRONGTYPE;
830                 }
831                 i = *request->requestvb->val.integer;
832                 if (i<1 || i>2) {  /* 'exec(1)' or 'shell(2)' only */
833                     netsnmp_set_request_error(reqinfo, request,
834                                               SNMP_ERR_WRONGVALUE);
835                     return SNMP_ERR_WRONGVALUE;
836                 }
837                 if (extension && extension->flags & NS_EXTEND_FLAGS_CONFIG) {
838                     /*
839                      * config entries are "permanent" so can't be changed
840                      */
841                     netsnmp_set_request_error(reqinfo, request,
842                                               SNMP_ERR_NOTWRITABLE);
843                     return SNMP_ERR_NOTWRITABLE;
844                 }
845                 break;
846 
847             case COLUMN_EXTCFG_RUNTYPE:
848                 if (request->requestvb->type != ASN_INTEGER) {
849                     netsnmp_set_request_error(reqinfo, request,
850                                               SNMP_ERR_WRONGTYPE);
851                     return SNMP_ERR_WRONGTYPE;
852                 }
853                 /*
854                  * 'run-on-read(1)', 'run-on-set(2)'
855                  *  or 'run-command(3)' only
856                  */
857                 i = *request->requestvb->val.integer;
858                 if (i<1 || i>3) {
859                     netsnmp_set_request_error(reqinfo, request,
860                                               SNMP_ERR_WRONGVALUE);
861                     return SNMP_ERR_WRONGVALUE;
862                 }
863                 /*
864                  * 'run-command(3)' can only be used with
865                  *  a pre-existing 'run-on-set(2)' entry.
866                  */
867                 if (i==3 && !(extension && (extension->flags & NS_EXTEND_FLAGS_WRITEABLE))) {
868                     netsnmp_set_request_error(reqinfo, request,
869                                               SNMP_ERR_INCONSISTENTVALUE);
870                     return SNMP_ERR_INCONSISTENTVALUE;
871                 }
872                 /*
873                  * 'run-command(3)' is the only valid assignment
874                  *  for permanent (i.e. config) entries
875                  */
876                 if ((extension && extension->flags & NS_EXTEND_FLAGS_CONFIG)
877                     && i!=3 ) {
878                     netsnmp_set_request_error(reqinfo, request,
879                                               SNMP_ERR_INCONSISTENTVALUE);
880                     return SNMP_ERR_INCONSISTENTVALUE;
881                 }
882                 break;
883 
884             case COLUMN_EXTCFG_STATUS:
885                 if (request->requestvb->type != ASN_INTEGER) {
886                     netsnmp_set_request_error(reqinfo, request,
887                                               SNMP_ERR_WRONGTYPE);
888                     return SNMP_ERR_WRONGTYPE;
889                 }
890                 i = *request->requestvb->val.integer;
891                 switch (i) {
892                 case RS_ACTIVE:
893                 case RS_NOTINSERVICE:
894                     if (!extension) {
895                         /* Must be used with existing rows */
896                         netsnmp_set_request_error(reqinfo, request,
897                                                   SNMP_ERR_INCONSISTENTVALUE);
898                         return SNMP_ERR_INCONSISTENTVALUE;
899                     }
900                     break;    /* OK */
901                 case RS_CREATEANDGO:
902                 case RS_CREATEANDWAIT:
903                     if (extension) {
904                         /* Can only be used to create new rows */
905                         netsnmp_set_request_error(reqinfo, request,
906                                                   SNMP_ERR_INCONSISTENTVALUE);
907                         return SNMP_ERR_INCONSISTENTVALUE;
908                     }
909                     break;
910                 case RS_DESTROY:
911                     break;
912                 default:
913                     netsnmp_set_request_error(reqinfo, request,
914                                               SNMP_ERR_WRONGVALUE);
915                     return SNMP_ERR_WRONGVALUE;
916                 }
917                 break;
918 
919             default:
920                 netsnmp_set_request_error(reqinfo, request,
921                                           SNMP_ERR_NOTWRITABLE);
922                 return SNMP_ERR_NOTWRITABLE;
923             }
924             break;
925 
926         case MODE_SET_RESERVE2:
927             switch (table_info->colnum) {
928             case COLUMN_EXTCFG_STATUS:
929                 i = *request->requestvb->val.integer;
930                 switch (i) {
931                 case RS_CREATEANDGO:
932                 case RS_CREATEANDWAIT:
933                     eptr = _find_extension_block( request->requestvb->name,
934                                                   request->requestvb->name_length );
935                     extension = _new_extension( (char *) table_info->indexes->val.string,
936                                                 0, eptr );
937                     if (!extension) {  /* failed */
938                         netsnmp_set_request_error(reqinfo, request,
939                                                   SNMP_ERR_RESOURCEUNAVAILABLE);
940                         return SNMP_ERR_RESOURCEUNAVAILABLE;
941                     }
942                     netsnmp_insert_table_row( request, extension->row );
943                 }
944             }
945             break;
946 
947         case MODE_SET_FREE:
948             switch (table_info->colnum) {
949             case COLUMN_EXTCFG_STATUS:
950                 i = *request->requestvb->val.integer;
951                 switch (i) {
952                 case RS_CREATEANDGO:
953                 case RS_CREATEANDWAIT:
954                     eptr = _find_extension_block( request->requestvb->name,
955                                                   request->requestvb->name_length );
956                     _free_extension( extension, eptr );
957                 }
958             }
959             break;
960 
961         case MODE_SET_ACTION:
962             switch (table_info->colnum) {
963             case COLUMN_EXTCFG_COMMAND:
964                 extension->old_command = extension->command;
965                 extension->command = netsnmp_strdup_and_null(
966                     request->requestvb->val.string,
967                     request->requestvb->val_len);
968                 break;
969             case COLUMN_EXTCFG_ARGS:
970                 extension->old_args = extension->args;
971                 extension->args = netsnmp_strdup_and_null(
972                     request->requestvb->val.string,
973                     request->requestvb->val_len);
974                 break;
975             case COLUMN_EXTCFG_INPUT:
976                 extension->old_input = extension->input;
977                 extension->input = netsnmp_strdup_and_null(
978                     request->requestvb->val.string,
979                     request->requestvb->val_len);
980                 break;
981             case COLUMN_EXTCFG_STATUS:
982                 i = *request->requestvb->val.integer;
983                 switch (i) {
984                 case RS_ACTIVE:
985                 case RS_CREATEANDGO:
986                     need_to_validate = 1;
987                 }
988                 break;
989             }
990             break;
991 
992         case MODE_SET_UNDO:
993             switch (table_info->colnum) {
994             case COLUMN_EXTCFG_COMMAND:
995                 if ( extension && extension->old_command ) {
996                     SNMP_FREE(extension->command);
997                     extension->command     = extension->old_command;
998                     extension->old_command = NULL;
999                 }
1000                 break;
1001             case COLUMN_EXTCFG_ARGS:
1002                 if ( extension && extension->old_args ) {
1003                     SNMP_FREE(extension->args);
1004                     extension->args     = extension->old_args;
1005                     extension->old_args = NULL;
1006                 }
1007                 break;
1008             case COLUMN_EXTCFG_INPUT:
1009                 if ( extension && extension->old_input ) {
1010                     SNMP_FREE(extension->input);
1011                     extension->input     = extension->old_input;
1012                     extension->old_input = NULL;
1013                 }
1014                 break;
1015             case COLUMN_EXTCFG_STATUS:
1016                 i = *request->requestvb->val.integer;
1017                 switch (i) {
1018                 case RS_CREATEANDGO:
1019                 case RS_CREATEANDWAIT:
1020                     eptr = _find_extension_block( request->requestvb->name,
1021                                                   request->requestvb->name_length );
1022                     _free_extension( extension, eptr );
1023                 }
1024                 break;
1025             }
1026             break;
1027 
1028         case MODE_SET_COMMIT:
1029             switch (table_info->colnum) {
1030             case COLUMN_EXTCFG_CACHETIME:
1031                 i = *request->requestvb->val.integer;
1032                 extension->cache->timeout = i;
1033                 break;
1034 
1035             case COLUMN_EXTCFG_RUNTYPE:
1036                 i = *request->requestvb->val.integer;
1037                 switch (i) {
1038                 case 1:
1039                     extension->flags &= ~NS_EXTEND_FLAGS_WRITEABLE;
1040                     break;
1041                 case 2:
1042                     extension->flags |=  NS_EXTEND_FLAGS_WRITEABLE;
1043                     break;
1044                 case 3:
1045                     (void)netsnmp_cache_check_and_reload( extension->cache );
1046                     break;
1047                 }
1048                 break;
1049 
1050             case COLUMN_EXTCFG_EXECTYPE:
1051                 i = *request->requestvb->val.integer;
1052                 if ( i == NS_EXTEND_ETYPE_SHELL )
1053                     extension->flags |=  NS_EXTEND_FLAGS_SHELL;
1054                 else
1055                     extension->flags &= ~NS_EXTEND_FLAGS_SHELL;
1056                 break;
1057 
1058             case COLUMN_EXTCFG_STATUS:
1059                 i = *request->requestvb->val.integer;
1060                 switch (i) {
1061                 case RS_ACTIVE:
1062                 case RS_CREATEANDGO:
1063                     extension->flags |= NS_EXTEND_FLAGS_ACTIVE;
1064                     break;
1065                 case RS_NOTINSERVICE:
1066                 case RS_CREATEANDWAIT:
1067                     extension->flags &= ~NS_EXTEND_FLAGS_ACTIVE;
1068                     break;
1069                 case RS_DESTROY:
1070                     eptr = _find_extension_block( request->requestvb->name,
1071                                                   request->requestvb->name_length );
1072                     _free_extension( extension, eptr );
1073                     break;
1074                 }
1075             }
1076             break;
1077 #endif /* !NETSNMP_NO_WRITE_SUPPORT and ENABLE_EXTEND_WRITE_ACCESS */
1078 
1079         default:
1080             netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
1081             return SNMP_ERR_GENERR;
1082         }
1083     }
1084 
1085 #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS
1086     /*
1087      * If we're marking a given row as active,
1088      *  then we need to check that it's ready.
1089      */
1090     if (need_to_validate) {
1091         for ( request=requests; request; request=request->next ) {
1092             if (request->processed)
1093                 continue;
1094             table_info = netsnmp_extract_table_info( request );
1095             extension  = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
1096             switch (table_info->colnum) {
1097             case COLUMN_EXTCFG_STATUS:
1098                 i = *request->requestvb->val.integer;
1099                 if (( i == RS_ACTIVE || i == RS_CREATEANDGO ) &&
1100                     !(extension && extension->command &&
1101                       extension->command[0] == '/' /* &&
1102                       is_executable(extension->command) */)) {
1103                     netsnmp_set_request_error(reqinfo, request,
1104                                               SNMP_ERR_INCONSISTENTVALUE);
1105                     return SNMP_ERR_INCONSISTENTVALUE;
1106                 }
1107             }
1108         }
1109     }
1110 #endif /* !NETSNMP_NO_WRITE_SUPPORT && ENABLE_EXTEND_WRITE_ACCESS */
1111 
1112     return SNMP_ERR_NOERROR;
1113 }
1114 
1115 
1116 int
handle_nsExtendOutput1Table(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)1117 handle_nsExtendOutput1Table(netsnmp_mib_handler          *handler,
1118                      netsnmp_handler_registration *reginfo,
1119                      netsnmp_agent_request_info   *reqinfo,
1120                      netsnmp_request_info         *requests)
1121 {
1122     netsnmp_request_info       *request;
1123     netsnmp_table_request_info *table_info;
1124     netsnmp_extend             *extension;
1125     int len;
1126 
1127     for ( request=requests; request; request=request->next ) {
1128         if (request->processed)
1129             continue;
1130         table_info = netsnmp_extract_table_info( request );
1131         extension  = (netsnmp_extend*)netsnmp_extract_table_row_data( request );
1132 
1133         DEBUGMSGTL(( "nsExtendTable:output1", "varbind: "));
1134         DEBUGMSGOID(("nsExtendTable:output1", request->requestvb->name,
1135                                               request->requestvb->name_length));
1136         DEBUGMSG((   "nsExtendTable:output1", "\n"));
1137 
1138         switch (reqinfo->mode) {
1139         case MODE_GET:
1140             if (!extension || !(extension->flags & NS_EXTEND_FLAGS_ACTIVE)) {
1141                 /*
1142                  * If this row is inactive, then skip it.
1143                  */
1144                 netsnmp_set_request_error(reqinfo, request,
1145                                           SNMP_NOSUCHINSTANCE);
1146                 continue;
1147             }
1148             if (!(extension->flags & NS_EXTEND_FLAGS_WRITEABLE) &&
1149                 (netsnmp_cache_check_and_reload( extension->cache ) < 0 )) {
1150                 /*
1151                  * If reloading the output cache of a 'run-on-read'
1152                  * entry fails, then skip it.
1153                  */
1154                 netsnmp_set_request_error(reqinfo, request,
1155                                           SNMP_NOSUCHINSTANCE);
1156                 continue;
1157             }
1158             if ((extension->flags & NS_EXTEND_FLAGS_WRITEABLE) &&
1159                 (netsnmp_cache_check_expired( extension->cache ) == 1 )) {
1160                 /*
1161                  * If the output cache of a 'run-on-write'
1162                  * entry has expired, then skip it.
1163                  */
1164                 netsnmp_set_request_error(reqinfo, request,
1165                                           SNMP_NOSUCHINSTANCE);
1166                 continue;
1167             }
1168 
1169             switch (table_info->colnum) {
1170             case COLUMN_EXTOUT1_OUTLEN:
1171                 snmp_set_var_typed_value(
1172                      request->requestvb, ASN_INTEGER,
1173                     (u_char*)&extension->out_len, sizeof(int));
1174                 break;
1175             case COLUMN_EXTOUT1_OUTPUT1:
1176                 /*
1177                  * If we've got more than one line,
1178                  * find the length of the first one.
1179                  * Otherwise find the length of the whole string.
1180                  */
1181                 if (extension->numlines > 1) {
1182                     len = (extension->lines[1])-(extension->output) -1;
1183                 } else if (extension->output) {
1184                     len = strlen(extension->output);
1185                 } else {
1186                     len = 0;
1187                 }
1188                 snmp_set_var_typed_value(
1189                      request->requestvb, ASN_OCTET_STR,
1190                      extension->output, len);
1191                 break;
1192             case COLUMN_EXTOUT1_OUTPUT2:
1193                 snmp_set_var_typed_value(
1194                      request->requestvb, ASN_OCTET_STR,
1195                      extension->output,
1196                     (extension->output)?extension->out_len:0);
1197                 break;
1198             case COLUMN_EXTOUT1_NUMLINES:
1199                 snmp_set_var_typed_value(
1200                      request->requestvb, ASN_INTEGER,
1201                     (u_char*)&extension->numlines, sizeof(int));
1202                 break;
1203             case COLUMN_EXTOUT1_RESULT:
1204                 snmp_set_var_typed_value(
1205                      request->requestvb, ASN_INTEGER,
1206                     (u_char*)&extension->result, sizeof(int));
1207                 break;
1208             default:
1209                 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
1210                 continue;
1211             }
1212             break;
1213         default:
1214             netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
1215             return SNMP_ERR_GENERR;
1216         }
1217     }
1218     return SNMP_ERR_NOERROR;
1219 }
1220 
1221 
1222         /*************************
1223          *
1224          *  Multi-line output table handler
1225          *  Most of the work is handled here.
1226          *
1227          *************************/
1228 
1229 
1230 /*
1231  *  Locate the appropriate entry for a given request
1232  */
1233 netsnmp_extend *
_extend_find_entry(netsnmp_request_info * request,netsnmp_table_request_info * table_info,int mode)1234 _extend_find_entry( netsnmp_request_info       *request,
1235                     netsnmp_table_request_info *table_info,
1236                     int mode  )
1237 {
1238     netsnmp_extend            *eptr;
1239     extend_registration_block *ereg;
1240     unsigned int line_idx;
1241     oid oid_buf[MAX_OID_LEN];
1242     int oid_len;
1243     int i;
1244     char  *token;
1245     size_t token_len;
1246 
1247     if (!request || !table_info || !table_info->indexes
1248                  || !table_info->indexes->next_variable) {
1249         DEBUGMSGTL(( "nsExtendTable:output2", "invalid invocation\n"));
1250         return NULL;
1251     }
1252 
1253     ereg = _find_extension_block( request->requestvb->name,
1254                                   request->requestvb->name_length );
1255 
1256     /***
1257      *  GET handling - find the exact entry being requested
1258      ***/
1259     if ( mode == MODE_GET ) {
1260         DEBUGMSGTL(( "nsExtendTable:output2", "GET: %s / %ld\n ",
1261                       table_info->indexes->val.string,
1262                      *table_info->indexes->next_variable->val.integer));
1263         for ( eptr = ereg->ehead; eptr; eptr = eptr->next ) {
1264             if ( !strcmp( eptr->token, (char *) table_info->indexes->val.string ))
1265                 break;
1266         }
1267 
1268         if ( eptr ) {
1269             /*
1270              * Ensure the output is available...
1271              */
1272             if (!(eptr->flags & NS_EXTEND_FLAGS_ACTIVE) ||
1273                (netsnmp_cache_check_and_reload( eptr->cache ) < 0 ))
1274                 return NULL;
1275 
1276             /*
1277              * ...and check the line requested is valid
1278              */
1279             line_idx = *table_info->indexes->next_variable->val.integer;
1280             if (line_idx < 1 || line_idx > eptr->numlines)
1281                 return NULL;
1282         }
1283     }
1284 
1285         /***
1286          *  GETNEXT handling - find the first suitable entry
1287          ***/
1288     else {
1289         if (!table_info->indexes->val_len ) {
1290             DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT: first entry\n"));
1291             /*
1292              * Beginning of the table - find the first active
1293              *  (and successful) entry, and use the first line of it
1294              */
1295             for (eptr = ereg->ehead; eptr; eptr = eptr->next ) {
1296                 if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
1297                     (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
1298                     line_idx = 1;
1299                     break;
1300                 }
1301             }
1302         } else {
1303             token     =  (char *) table_info->indexes->val.string;
1304             token_len =  table_info->indexes->val_len;
1305             line_idx  = *table_info->indexes->next_variable->val.integer;
1306             DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT: %s / %d\n ",
1307                           token, line_idx ));
1308             /*
1309              * Otherwise, find the first entry not earlier
1310              * than the requested token...
1311              */
1312             for (eptr = ereg->ehead; eptr; eptr = eptr->next ) {
1313                 if ( strlen(eptr->token) > token_len )
1314                     break;
1315                 if ( strlen(eptr->token) == token_len &&
1316                      strcmp(eptr->token, token) >= 0 )
1317                     break;
1318             }
1319             if (!eptr)
1320                 return NULL;    /* (assuming there is one) */
1321 
1322             /*
1323              * ... and make sure it's active & the output is available
1324              * (or use the first following entry that is)
1325              */
1326             for (    ; eptr; eptr = eptr->next ) {
1327                 if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
1328                     (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
1329                     break;
1330                 }
1331                 line_idx = 1;
1332             }
1333 
1334             if (!eptr)
1335                 return NULL;    /* (assuming there is one) */
1336 
1337             /*
1338              *  If we're working with the same entry that was requested,
1339              *  see whether we've reached the end of the output...
1340              */
1341             if (!strcmp( eptr->token, token )) {
1342                 if ( eptr->numlines <= line_idx ) {
1343                     /*
1344                      * ... and if so, move on to the first line
1345                      * of the next (active and successful) entry.
1346                      */
1347                     line_idx = 1;
1348                     for (eptr = eptr->next ; eptr; eptr = eptr->next ) {
1349                         if ((eptr->flags & NS_EXTEND_FLAGS_ACTIVE) &&
1350                             (netsnmp_cache_check_and_reload( eptr->cache ) >= 0 )) {
1351                             break;
1352                         }
1353                     }
1354                 } else {
1355                     /*
1356                      * Otherwise just use the next line of this entry.
1357                      */
1358                     line_idx++;
1359                 }
1360             }
1361             else {
1362                 /*
1363                  * If this is not the same entry that was requested,
1364                  * then we should return the first line.
1365                  */
1366                 line_idx = 1;
1367             }
1368         }
1369         if (eptr) {
1370             DEBUGMSGTL(( "nsExtendTable:output2", "GETNEXT -> %s / %d\n ",
1371                           eptr->token, line_idx));
1372             /*
1373              * Since we're processing a GETNEXT request,
1374              * now we've found the appropriate entry (and line),
1375              * we need to update the varbind OID ...
1376              */
1377             memset(oid_buf, 0, sizeof(oid_buf));
1378             oid_len = ereg->oid_len;
1379             memcpy( oid_buf, ereg->root_oid, oid_len*sizeof(oid));
1380             oid_buf[ oid_len++ ] = 4;    /* nsExtendOutput2Table */
1381             oid_buf[ oid_len++ ] = 1;    /* nsExtendOutput2Entry */
1382             oid_buf[ oid_len++ ] = COLUMN_EXTOUT2_OUTLINE;
1383                                          /* string token index */
1384             oid_buf[ oid_len++ ] = strlen(eptr->token);
1385             for ( i=0; i < (int)strlen(eptr->token); i++ )
1386                 oid_buf[ oid_len+i ] = eptr->token[i];
1387             oid_len += strlen( eptr->token );
1388                                          /* plus line number */
1389             oid_buf[ oid_len++ ] = line_idx;
1390             snmp_set_var_objid( request->requestvb, oid_buf, oid_len );
1391             /*
1392              * ... and index values to match.
1393              */
1394             snmp_set_var_value( table_info->indexes,
1395                                 eptr->token, strlen(eptr->token));
1396             snmp_set_var_value( table_info->indexes->next_variable,
1397                                 (const u_char*)&line_idx, sizeof(line_idx));
1398         }
1399     }
1400     return eptr;  /* Finally, signal success */
1401 }
1402 
1403 /*
1404  *  Multi-line output handler
1405  *  Locate the appropriate entry (using _extend_find_entry)
1406  *  and return the appropriate output line
1407  */
1408 int
handle_nsExtendOutput2Table(netsnmp_mib_handler * handler,netsnmp_handler_registration * reginfo,netsnmp_agent_request_info * reqinfo,netsnmp_request_info * requests)1409 handle_nsExtendOutput2Table(netsnmp_mib_handler          *handler,
1410                      netsnmp_handler_registration *reginfo,
1411                      netsnmp_agent_request_info   *reqinfo,
1412                      netsnmp_request_info         *requests)
1413 {
1414     netsnmp_request_info       *request;
1415     netsnmp_table_request_info *table_info;
1416     netsnmp_extend             *extension;
1417     char *cp;
1418     unsigned int line_idx;
1419     int len;
1420 
1421     for ( request=requests; request; request=request->next ) {
1422         if (request->processed)
1423             continue;
1424 
1425         table_info = netsnmp_extract_table_info( request );
1426         extension  = _extend_find_entry( request, table_info, reqinfo->mode );
1427 
1428         DEBUGMSGTL(( "nsExtendTable:output2", "varbind: "));
1429         DEBUGMSGOID(("nsExtendTable:output2", request->requestvb->name,
1430                                               request->requestvb->name_length));
1431         DEBUGMSG((   "nsExtendTable:output2", " (%s)\n",
1432                                     (extension) ? extension->token : "[none]"));
1433 
1434         if (!extension) {
1435             if (reqinfo->mode == MODE_GET)
1436                 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE);
1437             else
1438                 netsnmp_set_request_error(reqinfo, request, SNMP_ENDOFMIBVIEW);
1439             continue;
1440         }
1441 
1442         switch (reqinfo->mode) {
1443         case MODE_GET:
1444         case MODE_GETNEXT:
1445             switch (table_info->colnum) {
1446             case COLUMN_EXTOUT2_OUTLINE:
1447                 /*
1448                  * Determine which line we've been asked for....
1449                  */
1450                 line_idx = *table_info->indexes->next_variable->val.integer;
1451                 if (line_idx < 1 || line_idx > extension->numlines) {
1452                     netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE);
1453                     continue;
1454                 }
1455                 cp  = extension->lines[line_idx-1];
1456 
1457                 /*
1458                  * ... and how long it is.
1459                  */
1460                 if ( extension->numlines > line_idx )
1461                     len = (extension->lines[line_idx])-cp -1;
1462                 else if (cp)
1463                     len = strlen(cp);
1464                 else
1465                     len = 0;
1466 
1467                 snmp_set_var_typed_value( request->requestvb,
1468                                           ASN_OCTET_STR, cp, len );
1469                 break;
1470             default:
1471                 netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHOBJECT);
1472                 continue;
1473             }
1474             break;
1475         default:
1476             netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR);
1477             return SNMP_ERR_GENERR;
1478         }
1479     }
1480     return SNMP_ERR_NOERROR;
1481 }
1482 
1483 #ifndef USING_UCD_SNMP_EXTENSIBLE_MODULE
1484         /*************************
1485          *
1486          *  Compatability with the UCD extTable
1487          *
1488          *************************/
1489 
_get_cmdline(netsnmp_extend * extend)1490 char * _get_cmdline(netsnmp_extend *extend)
1491 {
1492     size_t          size;
1493     char           *newbuf;
1494     const char     *args = extend->args;
1495 
1496     if (args == NULL)
1497         /* Use empty string for processes without arguments. */
1498         args = "";
1499 
1500     size = strlen(extend->command) + strlen(args) + 2;
1501     if (size > cmdlinesize) {
1502         newbuf = realloc(cmdlinebuf, size);
1503         if (!newbuf) {
1504             free(cmdlinebuf);
1505             cmdlinebuf = NULL;
1506             cmdlinesize = 0;
1507             return NULL;
1508         }
1509         cmdlinebuf = newbuf;
1510         cmdlinesize = size;
1511     }
1512     sprintf(cmdlinebuf, "%s %s", extend->command, args);
1513     return cmdlinebuf;
1514 }
1515 
1516 u_char *
var_extensible_old(struct variable * vp,oid * name,size_t * length,int exact,size_t * var_len,WriteMethod ** write_method)1517 var_extensible_old(struct variable * vp,
1518                      oid * name,
1519                      size_t * length,
1520                      int exact,
1521                      size_t * var_len, WriteMethod ** write_method)
1522 {
1523     netsnmp_old_extend *exten = NULL;
1524     static long     long_ret;
1525     unsigned int idx;
1526     char         *cmdline;
1527 
1528     if (header_simple_table
1529         (vp, name, length, exact, var_len, write_method, num_compatability_entries))
1530         return (NULL);
1531 
1532     idx = name[*length-1] -1;
1533 	if (idx > max_compatability_entries)
1534 		return NULL;
1535     exten = &compatability_entries[idx];
1536     switch (vp->magic) {
1537     case MIBINDEX:
1538         long_ret = name[*length - 1];
1539         return (u_char *) &long_ret;
1540     case ERRORNAME:        /* name defined in config file */
1541         *var_len = strlen(exten->exec_entry->token);
1542         return ((u_char *) (exten->exec_entry->token));
1543     case SHELLCOMMAND:
1544         cmdline = _get_cmdline(exten->exec_entry);
1545         if (cmdline)
1546             *var_len = strlen(cmdline);
1547         return (u_char *) cmdline;
1548     case ERRORFLAG:        /* return code from the process */
1549         netsnmp_cache_check_and_reload( exten->exec_entry->cache );
1550         long_ret = exten->exec_entry->result;
1551         return (u_char *) &long_ret;
1552     case ERRORMSG:         /* first line of text returned from the process */
1553         netsnmp_cache_check_and_reload( exten->exec_entry->cache );
1554         if (exten->exec_entry->numlines > 1) {
1555             *var_len = (exten->exec_entry->lines[1])-
1556                 (exten->exec_entry->output) -1;
1557         } else if (exten->exec_entry->output) {
1558             *var_len = strlen(exten->exec_entry->output);
1559         } else {
1560             *var_len = 0;
1561         }
1562         return (u_char *) exten->exec_entry->output;
1563     case ERRORFIX:
1564         *write_method = fixExec2Error;
1565         long_return = 0;
1566         return (u_char *) &long_return;
1567 
1568     case ERRORFIXCMD:
1569         if (exten->efix_entry) {
1570             cmdline = _get_cmdline(exten->efix_entry);
1571             if (cmdline)
1572                 *var_len = strlen(cmdline);
1573             return (u_char *) cmdline;
1574         } else {
1575             *var_len = 0;
1576             return (u_char *) &long_return;  /* Just needs to be non-null! */
1577         }
1578     }
1579     return NULL;
1580 }
1581 
1582 
1583 int
fixExec2Error(int action,u_char * var_val,u_char var_val_type,size_t var_val_len,u_char * statP,oid * name,size_t name_len)1584 fixExec2Error(int action,
1585              u_char * var_val,
1586              u_char var_val_type,
1587              size_t var_val_len,
1588              u_char * statP, oid * name, size_t name_len)
1589 {
1590     netsnmp_old_extend *exten = NULL;
1591     unsigned int idx;
1592 
1593     idx = name[name_len-1] -1;
1594     exten = &compatability_entries[ idx ];
1595 
1596 #if !defined(NETSNMP_NO_WRITE_SUPPORT) && ENABLE_EXTEND_WRITE_ACCESS
1597     switch (action) {
1598     case MODE_SET_RESERVE1:
1599         if (var_val_type != ASN_INTEGER) {
1600             snmp_log(LOG_ERR, "Wrong type != int\n");
1601             return SNMP_ERR_WRONGTYPE;
1602         }
1603         idx = *((long *) var_val);
1604         if (idx != 1) {
1605             snmp_log(LOG_ERR, "Wrong value != 1\n");
1606             return SNMP_ERR_WRONGVALUE;
1607         }
1608         if (!exten || !exten->efix_entry) {
1609             snmp_log(LOG_ERR, "No command to run\n");
1610             return SNMP_ERR_GENERR;
1611         }
1612         return SNMP_ERR_NOERROR;
1613 
1614     case MODE_SET_COMMIT:
1615         netsnmp_cache_check_and_reload( exten->efix_entry->cache );
1616     }
1617 #endif /* !NETSNMP_NO_WRITE_SUPPORT && ENABLE_EXTEND_WRITE_ACCESS */
1618     return SNMP_ERR_NOERROR;
1619 }
1620 #endif /* USING_UCD_SNMP_EXTENSIBLE_MODULE */
1621