/src/net-snmp/agent/helpers/old_api.c
Line | Count | Source |
1 | | /* |
2 | | * Portions of this file are subject to the following copyright(s). See |
3 | | * the Net-SNMP's COPYING file for more details and other copyrights |
4 | | * that may apply: |
5 | | * |
6 | | * Portions of this file are copyrighted by: |
7 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
8 | | * Use is subject to license terms specified in the COPYING file |
9 | | * distributed with the Net-SNMP package. |
10 | | */ |
11 | | #include <net-snmp/net-snmp-config.h> |
12 | | |
13 | | #include <net-snmp/net-snmp-includes.h> |
14 | | #include <net-snmp/agent/net-snmp-agent-includes.h> |
15 | | |
16 | | #include <net-snmp/agent/old_api.h> |
17 | | |
18 | | #ifdef HAVE_STRING_H |
19 | | #include <string.h> |
20 | | #else |
21 | | #include <strings.h> |
22 | | #endif |
23 | | |
24 | | #include <net-snmp/agent/agent_callbacks.h> |
25 | | |
26 | | #include <stddef.h> |
27 | | |
28 | | /* |
29 | | * mib clients are passed a pointer to a oid buffer. Some mib clients |
30 | | * * (namely, those first noticed in mibII/vacm.c) modify this oid buffer |
31 | | * * before they determine if they really need to send results back out |
32 | | * * using it. If the master agent determined that the client was not the |
33 | | * * right one to talk with, it will use the same oid buffer to pass to the |
34 | | * * rest of the clients, which may not longer be valid. This should be |
35 | | * * fixed in all clients rather than the master. However, its not a |
36 | | * * particularly easy bug to track down so this saves debugging time at |
37 | | * * the expense of a few memcpy's. |
38 | | */ |
39 | | #define MIB_CLIENTS_ARE_EVIL 1 |
40 | | |
41 | | #ifdef HAVE_DMALLOC_H |
42 | | static void free_wrapper(void * p) |
43 | | { |
44 | | free(p); |
45 | | } |
46 | | #else |
47 | 0 | #define free_wrapper free |
48 | | #endif |
49 | | |
50 | | /* |
51 | | * don't use these! |
52 | | */ |
53 | | void set_current_agent_session(netsnmp_agent_session *asp); |
54 | | |
55 | | /** @defgroup old_api old_api |
56 | | * Calls mib module code written in the old style of code. |
57 | | * @ingroup handler |
58 | | * This is a backwards compatilibity module that allows code written |
59 | | * in the old API to be run under the new handler based architecture. |
60 | | * Use it by calling netsnmp_register_old_api(). |
61 | | * @{ |
62 | | */ |
63 | | |
64 | | /** returns a old_api handler that should be the final calling |
65 | | * handler. Don't use this function. Use the netsnmp_register_old_api() |
66 | | * function instead. |
67 | | */ |
68 | | netsnmp_mib_handler * |
69 | | get_old_api_handler(void) |
70 | 0 | { |
71 | 0 | return netsnmp_create_handler("old_api", netsnmp_old_api_helper); |
72 | 0 | } |
73 | | |
74 | | static void * |
75 | | netsnmp_clone_variable(void *p) |
76 | 0 | { |
77 | 0 | return netsnmp_duplicate_variable(p); |
78 | 0 | } |
79 | | |
80 | | struct variable * |
81 | | netsnmp_duplicate_variable(const struct variable *var) |
82 | 0 | { |
83 | 0 | struct variable *var2 = NULL; |
84 | | |
85 | 0 | if (var) { |
86 | 0 | const int varsize = offsetof(struct variable, name) + var->namelen * sizeof(var->name[0]); |
87 | 0 | var2 = malloc(varsize); |
88 | 0 | if (var2) |
89 | 0 | memcpy(var2, var, varsize); |
90 | 0 | } |
91 | 0 | return var2; |
92 | 0 | } |
93 | | |
94 | | /** Registers an old API set into the mib tree. Functionally this |
95 | | * mimics the old register_mib_context() function (and in fact the new |
96 | | * register_mib_context() function merely calls this new old_api one). |
97 | | */ |
98 | | int |
99 | | netsnmp_register_old_api(const char *moduleName, |
100 | | const struct variable *var, |
101 | | size_t varsize, |
102 | | size_t numvars, |
103 | | const oid * mibloc, |
104 | | size_t mibloclen, |
105 | | int priority, |
106 | | int range_subid, |
107 | | oid range_ubound, |
108 | | netsnmp_session * ss, |
109 | | const char *context, int timeout, int flags) |
110 | 0 | { |
111 | |
|
112 | 0 | unsigned int i; |
113 | 0 | int res; |
114 | | |
115 | | /* |
116 | | * register all subtree nodes |
117 | | */ |
118 | 0 | for (i = 0; i < numvars; i++) { |
119 | 0 | struct variable *vp; |
120 | 0 | netsnmp_handler_registration *reginfo = |
121 | 0 | SNMP_MALLOC_TYPEDEF(netsnmp_handler_registration); |
122 | 0 | if (reginfo == NULL) |
123 | 0 | return SNMP_ERR_GENERR; |
124 | | |
125 | 0 | vp = netsnmp_duplicate_variable((const struct variable *) |
126 | 0 | ((const char *) var + varsize * i)); |
127 | |
|
128 | 0 | if (vp == NULL) { |
129 | 0 | SNMP_FREE(reginfo); |
130 | 0 | return SNMP_ERR_GENERR; |
131 | 0 | } |
132 | | |
133 | 0 | reginfo->handler = get_old_api_handler(); |
134 | 0 | reginfo->handlerName = strdup(moduleName); |
135 | 0 | reginfo->rootoid_len = (mibloclen + vp->namelen); |
136 | 0 | reginfo->rootoid = |
137 | 0 | (oid *) malloc(reginfo->rootoid_len * sizeof(oid)); |
138 | 0 | if (NULL == reginfo->handler || NULL == reginfo->handlerName || |
139 | 0 | NULL == reginfo->rootoid) { |
140 | 0 | netsnmp_handler_free(reginfo->handler); |
141 | 0 | SNMP_FREE(vp); |
142 | 0 | SNMP_FREE(reginfo->handlerName); |
143 | 0 | SNMP_FREE(reginfo->rootoid); |
144 | 0 | SNMP_FREE(reginfo); |
145 | 0 | return SNMP_ERR_GENERR; |
146 | 0 | } |
147 | | |
148 | 0 | memcpy(reginfo->rootoid, mibloc, mibloclen * sizeof(oid)); |
149 | 0 | memcpy(reginfo->rootoid + mibloclen, vp->name, vp->namelen |
150 | 0 | * sizeof(oid)); |
151 | 0 | reginfo->handler->myvoid = (void *) vp; |
152 | 0 | reginfo->handler->data_clone = netsnmp_clone_variable; |
153 | 0 | reginfo->handler->data_free = free; |
154 | |
|
155 | 0 | reginfo->priority = priority; |
156 | 0 | reginfo->range_subid = range_subid; |
157 | |
|
158 | 0 | reginfo->range_ubound = range_ubound; |
159 | 0 | reginfo->timeout = timeout; |
160 | 0 | reginfo->contextName = (context) ? strdup(context) : NULL; |
161 | 0 | reginfo->modes = vp->acl == NETSNMP_OLDAPI_RONLY ? HANDLER_CAN_RONLY : |
162 | 0 | HANDLER_CAN_RWRITE; |
163 | | |
164 | | /* |
165 | | * register ourselves in the mib tree |
166 | | */ |
167 | 0 | res = netsnmp_register_handler(reginfo); |
168 | 0 | if (MIB_REGISTERED_OK != res) { |
169 | | /** reginfo already freed on error. */ |
170 | 0 | snmp_log(LOG_WARNING, "old_api handler registration failed\n"); |
171 | 0 | return res; |
172 | 0 | } |
173 | 0 | } |
174 | 0 | return SNMPERR_SUCCESS; |
175 | 0 | } |
176 | | |
177 | | /** registers a row within a mib table */ |
178 | | int |
179 | | netsnmp_register_mib_table_row(const char *moduleName, |
180 | | const struct variable *var, |
181 | | size_t varsize, |
182 | | size_t numvars, |
183 | | oid * mibloc, |
184 | | size_t mibloclen, |
185 | | int priority, |
186 | | int var_subid, |
187 | | netsnmp_session * ss, |
188 | | const char *context, int timeout, int flags) |
189 | 0 | { |
190 | 0 | unsigned int i = 0, rc = 0; |
191 | 0 | oid ubound = 0; |
192 | |
|
193 | 0 | for (i = 0; i < numvars; i++) { |
194 | 0 | const struct variable *vr = |
195 | 0 | (const struct variable *) ((const char *) var + (i * varsize)); |
196 | 0 | netsnmp_handler_registration *r; |
197 | 0 | if ( var_subid > (int)mibloclen ) { |
198 | 0 | break; /* doesn't make sense */ |
199 | 0 | } |
200 | 0 | r = SNMP_MALLOC_TYPEDEF(netsnmp_handler_registration); |
201 | |
|
202 | 0 | if (r == NULL) { |
203 | | /* |
204 | | * Unregister whatever we have registered so far, and |
205 | | * return an error. |
206 | | */ |
207 | 0 | snmp_log(LOG_ERR, "mib table row registration failed\n"); |
208 | 0 | rc = MIB_REGISTRATION_FAILED; |
209 | 0 | break; |
210 | 0 | } |
211 | | |
212 | 0 | r->handler = get_old_api_handler(); |
213 | 0 | r->handlerName = strdup(moduleName); |
214 | 0 | r->rootoid_len = mibloclen; |
215 | 0 | r->rootoid = (oid *) malloc(r->rootoid_len * sizeof(oid)); |
216 | 0 | if (r->handler == NULL || r->handlerName == NULL || |
217 | 0 | r->rootoid == NULL) { |
218 | 0 | netsnmp_handler_registration_free(r); |
219 | 0 | rc = MIB_REGISTRATION_FAILED; |
220 | 0 | break; |
221 | 0 | } |
222 | 0 | memcpy(r->rootoid, mibloc, mibloclen * sizeof(oid)); |
223 | 0 | memcpy((u_char *) (r->rootoid + (var_subid - vr->namelen)), vr->name, |
224 | 0 | vr->namelen * sizeof(oid)); |
225 | 0 | DEBUGMSGTL(("netsnmp_register_mib_table_row", "rootoid ")); |
226 | 0 | DEBUGMSGOID(("netsnmp_register_mib_table_row", r->rootoid, |
227 | 0 | r->rootoid_len)); |
228 | 0 | DEBUGMSG(("netsnmp_register_mib_table_row", "(%d)\n", |
229 | 0 | (var_subid - vr->namelen))); |
230 | 0 | r->handler->myvoid = netsnmp_duplicate_variable(vr); |
231 | 0 | r->handler->data_clone = netsnmp_clone_variable; |
232 | 0 | r->handler->data_free = free; |
233 | |
|
234 | 0 | r->contextName = (context) ? strdup(context) : NULL; |
235 | 0 | if (r->handler->myvoid == NULL || |
236 | 0 | (context != NULL && r->contextName == NULL)) { |
237 | 0 | netsnmp_handler_registration_free(r); |
238 | 0 | rc = MIB_REGISTRATION_FAILED; |
239 | 0 | break; |
240 | 0 | } |
241 | | |
242 | 0 | r->priority = priority; |
243 | 0 | r->range_subid = 0; /* var_subid; */ |
244 | 0 | r->range_ubound = 0; /* range_ubound; */ |
245 | 0 | r->timeout = timeout; |
246 | 0 | r->modes = HANDLER_CAN_RWRITE; |
247 | | |
248 | | /* |
249 | | * Register this column and row |
250 | | */ |
251 | 0 | if ((rc = |
252 | 0 | netsnmp_register_handler_nocallback(r)) != |
253 | 0 | MIB_REGISTERED_OK) { |
254 | 0 | snmp_log(LOG_ERR, "mib table row registration failed\n"); |
255 | 0 | DEBUGMSGTL(("netsnmp_register_mib_table_row", |
256 | 0 | "register failed %d\n", rc)); |
257 | | /** reginfo already freed */ |
258 | 0 | break; |
259 | 0 | } |
260 | | |
261 | 0 | if (vr->namelen > 0) { |
262 | 0 | if (vr->name[vr->namelen - 1] > ubound) { |
263 | 0 | ubound = vr->name[vr->namelen - 1]; |
264 | 0 | } |
265 | 0 | } |
266 | 0 | } |
267 | |
|
268 | 0 | if (rc == MIB_REGISTERED_OK) { |
269 | 0 | struct register_parameters reg_parms; |
270 | |
|
271 | 0 | reg_parms.name = mibloc; |
272 | 0 | reg_parms.namelen = mibloclen; |
273 | 0 | reg_parms.priority = priority; |
274 | 0 | reg_parms.flags = (u_char) flags; |
275 | 0 | reg_parms.range_subid = var_subid; |
276 | 0 | reg_parms.range_ubound = ubound; |
277 | 0 | reg_parms.timeout = timeout; |
278 | 0 | reg_parms.contextName = context; |
279 | 0 | rc = snmp_call_callbacks(SNMP_CALLBACK_APPLICATION, |
280 | 0 | SNMPD_CALLBACK_REGISTER_OID, ®_parms); |
281 | 0 | } |
282 | |
|
283 | 0 | return rc; |
284 | 0 | } |
285 | | |
286 | | /** implements the old_api handler */ |
287 | | int |
288 | | netsnmp_old_api_helper(netsnmp_mib_handler *handler, |
289 | | netsnmp_handler_registration *reginfo, |
290 | | netsnmp_agent_request_info *reqinfo, |
291 | | netsnmp_request_info *requests) |
292 | 0 | { |
293 | |
|
294 | 0 | #ifdef MIB_CLIENTS_ARE_EVIL |
295 | 0 | oid save[MAX_OID_LEN]; |
296 | 0 | size_t savelen = 0; |
297 | 0 | #endif |
298 | 0 | struct variable compat_var, *cvp = &compat_var; |
299 | 0 | int exact = 1; |
300 | 0 | int status; |
301 | |
|
302 | 0 | struct variable *const vp = handler->myvoid; |
303 | 0 | netsnmp_old_api_cache *cacheptr; |
304 | 0 | netsnmp_agent_session *oldasp = NULL; |
305 | 0 | u_char *access = NULL; |
306 | 0 | WriteMethod *write_method = NULL; |
307 | 0 | size_t len; |
308 | 0 | size_t tmp_len; |
309 | 0 | oid tmp_name[MAX_OID_LEN]; |
310 | |
|
311 | 0 | snmp_call_callbacks(SNMP_CALLBACK_LIBRARY, |
312 | 0 | SNMP_CALLBACK_MIB_REQUEST_INFO, |
313 | 0 | reqinfo); |
314 | | /* |
315 | | * create old variable structure with right information |
316 | | */ |
317 | 0 | memcpy(cvp->name, reginfo->rootoid, |
318 | 0 | reginfo->rootoid_len * sizeof(oid)); |
319 | 0 | cvp->namelen = reginfo->rootoid_len; |
320 | 0 | cvp->type = vp->type; |
321 | 0 | cvp->magic = vp->magic; |
322 | 0 | cvp->acl = vp->acl; |
323 | 0 | cvp->findVar = vp->findVar; |
324 | |
|
325 | 0 | switch (reqinfo->mode) { |
326 | 0 | case MODE_GETNEXT: |
327 | 0 | case MODE_GETBULK: |
328 | 0 | exact = 0; |
329 | 0 | } |
330 | |
|
331 | 0 | for (; requests; requests = requests->next) { |
332 | |
|
333 | 0 | #ifdef MIB_CLIENTS_ARE_EVIL |
334 | 0 | savelen = requests->requestvb->name_length; |
335 | 0 | memcpy(save, requests->requestvb->name, savelen * sizeof(oid)); |
336 | 0 | #endif |
337 | |
|
338 | 0 | switch (reqinfo->mode) { |
339 | 0 | case MODE_GET: |
340 | 0 | case MODE_GETNEXT: |
341 | 0 | #ifndef NETSNMP_NO_WRITE_SUPPORT |
342 | 0 | case MODE_SET_RESERVE1: |
343 | 0 | #endif /* !NETSNMP_NO_WRITE_SUPPORT */ |
344 | | /* |
345 | | * Actually call the old mib-module function |
346 | | */ |
347 | 0 | if (vp->findVar) { |
348 | 0 | tmp_len = requests->requestvb->name_length*sizeof(oid); |
349 | 0 | memcpy(tmp_name, requests->requestvb->name, tmp_len); |
350 | | /** clear the rest of tmp_name to keep valgrind happy */ |
351 | 0 | memset(&tmp_name[requests->requestvb->name_length], 0x0, |
352 | 0 | sizeof(tmp_name)-tmp_len); |
353 | 0 | tmp_len = requests->requestvb->name_length; |
354 | 0 | access = (*(vp->findVar)) (cvp, tmp_name, &tmp_len, |
355 | 0 | exact, &len, &write_method); |
356 | 0 | snmp_set_var_objid( requests->requestvb, tmp_name, tmp_len ); |
357 | 0 | } |
358 | 0 | else |
359 | 0 | access = NULL; |
360 | |
|
361 | | #ifdef WWW_FIX |
362 | | if (IS_DELEGATED(cvp->type)) { |
363 | | add_method = (AddVarMethod *) statP; |
364 | | requests->delayed = 1; |
365 | | have_delegated = 1; |
366 | | continue; /* WWW: This may not get to the right place */ |
367 | | } |
368 | | #endif |
369 | | |
370 | | /* |
371 | | * WWW: end range checking |
372 | | */ |
373 | 0 | if (access) { |
374 | | /* |
375 | | * result returned |
376 | | */ |
377 | 0 | #ifndef NETSNMP_NO_WRITE_SUPPORT |
378 | 0 | if (reqinfo->mode != MODE_SET_RESERVE1) |
379 | 0 | #endif /* !NETSNMP_NO_WRITE_SUPPORT */ |
380 | 0 | snmp_set_var_typed_value(requests->requestvb, |
381 | 0 | cvp->type, access, len); |
382 | 0 | } else { |
383 | | /* |
384 | | * no result returned |
385 | | */ |
386 | 0 | #ifdef MIB_CLIENTS_ARE_EVIL |
387 | 0 | if (access == NULL) { |
388 | 0 | if (netsnmp_oid_equals(requests->requestvb->name, |
389 | 0 | requests->requestvb->name_length, |
390 | 0 | save, savelen) != 0) { |
391 | 0 | DEBUGMSGTL(("old_api", "evil_client: %s\n", |
392 | 0 | reginfo->handlerName)); |
393 | 0 | memcpy(requests->requestvb->name, save, |
394 | 0 | savelen * sizeof(oid)); |
395 | 0 | requests->requestvb->name_length = savelen; |
396 | 0 | } |
397 | 0 | } |
398 | 0 | #endif |
399 | 0 | } |
400 | | |
401 | | /* |
402 | | * AAA: fall through for everything that is a set (see BBB) |
403 | | */ |
404 | 0 | #ifndef NETSNMP_NO_WRITE_SUPPORT |
405 | 0 | if (reqinfo->mode != MODE_SET_RESERVE1) |
406 | 0 | #endif /* !NETSNMP_NO_WRITE_SUPPORT */ |
407 | 0 | break; |
408 | | |
409 | 0 | cacheptr = SNMP_MALLOC_TYPEDEF(netsnmp_old_api_cache); |
410 | 0 | if (!cacheptr) |
411 | 0 | return netsnmp_set_request_error(reqinfo, requests, |
412 | 0 | SNMP_ERR_RESOURCEUNAVAILABLE); |
413 | 0 | cacheptr->data = access; |
414 | 0 | cacheptr->write_method = write_method; |
415 | 0 | write_method = NULL; |
416 | 0 | netsnmp_request_add_list_data(requests, |
417 | 0 | netsnmp_create_data_list |
418 | 0 | (OLD_API_NAME, cacheptr, |
419 | 0 | &free_wrapper)); |
420 | | /* |
421 | | * BBB: fall through for everything that is a set (see AAA) |
422 | | */ |
423 | 0 | NETSNMP_FALLTHROUGH; |
424 | |
|
425 | 0 | default: |
426 | | /* |
427 | | * WWW: explicitly list the SET conditions |
428 | | */ |
429 | | /* |
430 | | * (the rest of the) SET conditions |
431 | | */ |
432 | 0 | cacheptr = |
433 | 0 | (netsnmp_old_api_cache *) |
434 | 0 | netsnmp_request_get_list_data(requests, OLD_API_NAME); |
435 | |
|
436 | 0 | if (cacheptr == NULL || cacheptr->write_method == NULL) { |
437 | | /* |
438 | | * WWW: try to set ourselves if possible? |
439 | | */ |
440 | 0 | return netsnmp_set_request_error(reqinfo, requests, |
441 | 0 | SNMP_ERR_NOTWRITABLE); |
442 | 0 | } |
443 | | |
444 | 0 | oldasp = netsnmp_get_current_agent_session(); |
445 | 0 | set_current_agent_session(reqinfo->asp); |
446 | 0 | status = |
447 | 0 | (*(cacheptr->write_method)) (reqinfo->mode, |
448 | 0 | requests->requestvb->val. |
449 | 0 | string, |
450 | 0 | requests->requestvb->type, |
451 | 0 | requests->requestvb->val_len, |
452 | 0 | cacheptr->data, |
453 | 0 | requests->requestvb->name, |
454 | 0 | requests->requestvb-> |
455 | 0 | name_length); |
456 | 0 | set_current_agent_session(oldasp); |
457 | |
|
458 | 0 | if (status != SNMP_ERR_NOERROR) { |
459 | 0 | netsnmp_set_request_error(reqinfo, requests, status); |
460 | 0 | } |
461 | | |
462 | | /* |
463 | | * clean up is done by the automatic freeing of the |
464 | | * cache stored in the request. |
465 | | */ |
466 | |
|
467 | 0 | break; |
468 | 0 | } |
469 | 0 | } |
470 | 0 | return SNMP_ERR_NOERROR; |
471 | 0 | } |
472 | | |
473 | | /** @} */ |
474 | | |
475 | | /* |
476 | | * don't use this! |
477 | | */ |
478 | | static netsnmp_agent_session *current_agent_session = NULL; |
479 | | netsnmp_agent_session * |
480 | | netsnmp_get_current_agent_session(void) |
481 | 0 | { |
482 | 0 | return current_agent_session; |
483 | 0 | } |
484 | | |
485 | | /* |
486 | | * don't use this! |
487 | | */ |
488 | | void |
489 | | set_current_agent_session(netsnmp_agent_session *asp) |
490 | 0 | { |
491 | 0 | current_agent_session = asp; |
492 | 0 | } |