/src/net-snmp/snmplib/vacm.c
Line | Count | Source |
1 | | /* Portions of this file are subject to the following copyright(s). See |
2 | | * the Net-SNMP's COPYING file for more details and other copyrights |
3 | | * that may apply: |
4 | | */ |
5 | | /* |
6 | | * Portions of this file are copyrighted by: |
7 | | * Copyright © 2003 Sun Microsystems, 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 | | * Portions of this file are copyrighted by: |
12 | | * Copyright (c) 2016 VMware, Inc. All rights reserved. |
13 | | * Use is subject to license terms specified in the COPYING file |
14 | | * distributed with the Net-SNMP package. |
15 | | */ |
16 | | |
17 | | /* |
18 | | * vacm.c |
19 | | * |
20 | | * SNMPv3 View-based Access Control Model |
21 | | */ |
22 | | |
23 | | #include <net-snmp/net-snmp-config.h> |
24 | | |
25 | | #ifdef HAVE_STDLIB_H |
26 | | #include <stdlib.h> |
27 | | #endif |
28 | | #ifdef HAVE_STRING_H |
29 | | #include <string.h> |
30 | | #else |
31 | | #include <strings.h> |
32 | | #endif |
33 | | #ifdef HAVE_UNISTD_H |
34 | | #include <unistd.h> |
35 | | #endif |
36 | | #include <sys/types.h> |
37 | | #include <stdio.h> |
38 | | #ifdef TIME_WITH_SYS_TIME |
39 | | # include <sys/time.h> |
40 | | # include <time.h> |
41 | | #else |
42 | | # ifdef HAVE_SYS_TIME_H |
43 | | # include <sys/time.h> |
44 | | # else |
45 | | # include <time.h> |
46 | | # endif |
47 | | #endif |
48 | | |
49 | | #ifdef HAVE_NETINET_IN_H |
50 | | #include <netinet/in.h> |
51 | | #endif |
52 | | |
53 | | #include <ctype.h> |
54 | | |
55 | | #include <net-snmp/types.h> |
56 | | #include <net-snmp/output_api.h> |
57 | | #include <net-snmp/config_api.h> |
58 | | |
59 | | #include <net-snmp/library/snmp.h> |
60 | | #include <net-snmp/library/snmp-tc.h> |
61 | | #include <net-snmp/library/snmp_api.h> |
62 | | #include <net-snmp/library/system.h> /* strlcpy() */ |
63 | | #include <net-snmp/library/tools.h> |
64 | | #include <net-snmp/library/vacm.h> |
65 | | |
66 | | static struct vacm_viewEntry *viewList = NULL, *viewScanPtr = NULL; |
67 | | static struct vacm_accessEntry *accessList = NULL, *accessScanPtr = NULL; |
68 | | static struct vacm_groupEntry *groupList = NULL, *groupScanPtr = NULL; |
69 | | |
70 | | /* |
71 | | * Macro to extend view masks with 1 bits when shorter than subtree lengths |
72 | | * REF: vacmViewTreeFamilyMask [RFC3415], snmpNotifyFilterMask [RFC3413] |
73 | | */ |
74 | | |
75 | | #define VIEW_MASK(viewPtr, idx, mask) \ |
76 | 0 | ((idx >= viewPtr->viewMaskLen) ? mask : (viewPtr->viewMask[idx] & mask)) |
77 | | |
78 | | /** |
79 | | * Initializes the VACM code. |
80 | | * Specifically: |
81 | | * - adds a set of enums mapping view numbers to human readable names |
82 | | */ |
83 | | void |
84 | | init_vacm(void) |
85 | 0 | { |
86 | | /* views for access via get/set/send-notifications */ |
87 | 0 | se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("read"), |
88 | 0 | VACM_VIEW_READ); |
89 | 0 | se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("write"), |
90 | 0 | VACM_VIEW_WRITE); |
91 | 0 | se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("notify"), |
92 | 0 | VACM_VIEW_NOTIFY); |
93 | | |
94 | | /* views for permissions when receiving notifications */ |
95 | 0 | se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("log"), |
96 | 0 | VACM_VIEW_LOG); |
97 | 0 | se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("execute"), |
98 | 0 | VACM_VIEW_EXECUTE); |
99 | 0 | se_add_pair_to_slist(VACM_VIEW_ENUM_NAME, strdup("net"), |
100 | 0 | VACM_VIEW_NET); |
101 | 0 | } |
102 | | |
103 | | void |
104 | | vacm_save(const char *token, const char *type) |
105 | 0 | { |
106 | 0 | struct vacm_viewEntry *vptr; |
107 | 0 | struct vacm_accessEntry *aptr; |
108 | 0 | struct vacm_groupEntry *gptr; |
109 | 0 | int i; |
110 | |
|
111 | 0 | for (vptr = viewList; vptr != NULL; vptr = vptr->next) { |
112 | 0 | if (vptr->viewStorageType == ST_NONVOLATILE) |
113 | 0 | vacm_save_view(vptr, token, type); |
114 | 0 | } |
115 | |
|
116 | 0 | for (aptr = accessList; aptr != NULL; aptr = aptr->next) { |
117 | 0 | if (aptr->storageType == ST_NONVOLATILE) { |
118 | | /* Store the standard views (if set) */ |
119 | 0 | if ( aptr->views[VACM_VIEW_READ ][0] || |
120 | 0 | aptr->views[VACM_VIEW_WRITE ][0] || |
121 | 0 | aptr->views[VACM_VIEW_NOTIFY][0] ) |
122 | 0 | vacm_save_access(aptr, token, type); |
123 | | /* Store any other (valid) access views */ |
124 | 0 | for ( i=VACM_VIEW_NOTIFY+1; i<VACM_MAX_VIEWS; i++ ) { |
125 | 0 | if ( aptr->views[i][0] ) |
126 | 0 | vacm_save_auth_access(aptr, token, type, i); |
127 | 0 | } |
128 | 0 | } |
129 | 0 | } |
130 | |
|
131 | 0 | for (gptr = groupList; gptr != NULL; gptr = gptr->next) { |
132 | 0 | if (gptr->storageType == ST_NONVOLATILE) |
133 | 0 | vacm_save_group(gptr, token, type); |
134 | 0 | } |
135 | 0 | } |
136 | | |
137 | | /* |
138 | | * vacm_save_view(): saves a view entry to the persistent cache |
139 | | */ |
140 | | void |
141 | | vacm_save_view(struct vacm_viewEntry *view, const char *token, |
142 | | const char *type) |
143 | 0 | { |
144 | 0 | char line[4096]; |
145 | 0 | char *cptr; |
146 | |
|
147 | 0 | memset(line, 0, sizeof(line)); |
148 | 0 | snprintf(line, sizeof(line), "%s%s %d %d %d ", token, "View", |
149 | 0 | view->viewStatus, view->viewStorageType, view->viewType); |
150 | 0 | line[ sizeof(line)-1 ] = 0; |
151 | 0 | cptr = &line[strlen(line)]; /* the NULL */ |
152 | |
|
153 | 0 | cptr = |
154 | 0 | read_config_save_octet_string(cptr, (u_char *) view->viewName + 1, |
155 | 0 | view->viewName[0]); |
156 | 0 | *cptr++ = ' '; |
157 | 0 | cptr = |
158 | 0 | read_config_save_objid(cptr, view->viewSubtree+1, |
159 | 0 | view->viewSubtreeLen-1); |
160 | 0 | *cptr++ = ' '; |
161 | 0 | cptr = read_config_save_octet_string(cptr, (u_char *) view->viewMask, |
162 | 0 | view->viewMaskLen); |
163 | |
|
164 | 0 | read_config_store(type, line); |
165 | 0 | } |
166 | | |
167 | | void |
168 | | vacm_parse_config_view(const char *token, const char *line) |
169 | 0 | { |
170 | 0 | struct vacm_viewEntry view; |
171 | 0 | struct vacm_viewEntry *vptr; |
172 | 0 | char *viewName = (char *) &view.viewName; |
173 | 0 | oid *viewSubtree = (oid *) & view.viewSubtree; |
174 | 0 | u_char *viewMask; |
175 | 0 | size_t len; |
176 | |
|
177 | 0 | view.viewStatus = atoi(line); |
178 | 0 | line = skip_token_const(line); |
179 | 0 | if (!line) { |
180 | 0 | config_perror("incomplete line"); |
181 | 0 | return; |
182 | 0 | } |
183 | 0 | view.viewStorageType = atoi(line); |
184 | 0 | line = skip_token_const(line); |
185 | 0 | if (!line) { |
186 | 0 | config_perror("incomplete line"); |
187 | 0 | return; |
188 | 0 | } |
189 | 0 | view.viewType = atoi(line); |
190 | 0 | line = skip_token_const(line); |
191 | 0 | len = sizeof(view.viewName); |
192 | 0 | line = |
193 | 0 | read_config_read_octet_string(line, (u_char **) & viewName, &len); |
194 | 0 | view.viewSubtreeLen = MAX_OID_LEN + 1; |
195 | 0 | line = |
196 | 0 | read_config_read_objid_const(line, (oid **) & viewSubtree, |
197 | 0 | &view.viewSubtreeLen); |
198 | |
|
199 | 0 | vptr = |
200 | 0 | vacm_createViewEntry(view.viewName, view.viewSubtree, |
201 | 0 | view.viewSubtreeLen); |
202 | 0 | if (!vptr) { |
203 | 0 | return; |
204 | 0 | } |
205 | | |
206 | 0 | vptr->viewStatus = view.viewStatus; |
207 | 0 | vptr->viewStorageType = view.viewStorageType; |
208 | 0 | vptr->viewType = view.viewType; |
209 | 0 | viewMask = vptr->viewMask; |
210 | 0 | vptr->viewMaskLen = sizeof(vptr->viewMask); |
211 | 0 | line = |
212 | 0 | read_config_read_octet_string(line, &viewMask, &vptr->viewMaskLen); |
213 | 0 | } |
214 | | |
215 | | /* |
216 | | * vacm_save_access(): saves an access entry to the persistent cache |
217 | | */ |
218 | | void |
219 | | vacm_save_access(struct vacm_accessEntry *access_entry, const char *token, |
220 | | const char *type) |
221 | 0 | { |
222 | 0 | char line[4096]; |
223 | 0 | char *cptr; |
224 | |
|
225 | 0 | memset(line, 0, sizeof(line)); |
226 | 0 | snprintf(line, sizeof(line), "%s%s %d %d %d %d %d ", |
227 | 0 | token, "Access", access_entry->status, |
228 | 0 | access_entry->storageType, access_entry->securityModel, |
229 | 0 | access_entry->securityLevel, access_entry->contextMatch); |
230 | 0 | line[ sizeof(line)-1 ] = 0; |
231 | 0 | cptr = &line[strlen(line)]; /* the NULL */ |
232 | 0 | cptr = |
233 | 0 | read_config_save_octet_string(cptr, |
234 | 0 | (u_char *) access_entry->groupName + 1, |
235 | 0 | access_entry->groupName[0] + 1); |
236 | 0 | *cptr++ = ' '; |
237 | 0 | cptr = |
238 | 0 | read_config_save_octet_string(cptr, |
239 | 0 | (u_char *) access_entry->contextPrefix + 1, |
240 | 0 | access_entry->contextPrefix[0] + 1); |
241 | |
|
242 | 0 | *cptr++ = ' '; |
243 | 0 | cptr = read_config_save_octet_string(cptr, (u_char *) access_entry->views[VACM_VIEW_READ], |
244 | 0 | strlen(access_entry->views[VACM_VIEW_READ]) + 1); |
245 | 0 | *cptr++ = ' '; |
246 | 0 | cptr = |
247 | 0 | read_config_save_octet_string(cptr, (u_char *) access_entry->views[VACM_VIEW_WRITE], |
248 | 0 | strlen(access_entry->views[VACM_VIEW_WRITE]) + 1); |
249 | 0 | *cptr++ = ' '; |
250 | 0 | cptr = |
251 | 0 | read_config_save_octet_string(cptr, (u_char *) access_entry->views[VACM_VIEW_NOTIFY], |
252 | 0 | strlen(access_entry->views[VACM_VIEW_NOTIFY]) + 1); |
253 | |
|
254 | 0 | read_config_store(type, line); |
255 | 0 | } |
256 | | |
257 | | void |
258 | | vacm_save_auth_access(struct vacm_accessEntry *access_entry, |
259 | | const char *token, const char *type, int authtype) |
260 | 0 | { |
261 | 0 | char line[4096]; |
262 | 0 | char *cptr; |
263 | |
|
264 | 0 | memset(line, 0, sizeof(line)); |
265 | 0 | snprintf(line, sizeof(line), "%s%s %d %d %d %d %d ", |
266 | 0 | token, "AuthAccess", access_entry->status, |
267 | 0 | access_entry->storageType, access_entry->securityModel, |
268 | 0 | access_entry->securityLevel, access_entry->contextMatch); |
269 | 0 | line[ sizeof(line)-1 ] = 0; |
270 | 0 | cptr = &line[strlen(line)]; /* the NULL */ |
271 | 0 | cptr = |
272 | 0 | read_config_save_octet_string(cptr, |
273 | 0 | (u_char *) access_entry->groupName + 1, |
274 | 0 | access_entry->groupName[0] + 1); |
275 | 0 | *cptr++ = ' '; |
276 | 0 | cptr = |
277 | 0 | read_config_save_octet_string(cptr, |
278 | 0 | (u_char *) access_entry->contextPrefix + 1, |
279 | 0 | access_entry->contextPrefix[0] + 1); |
280 | |
|
281 | 0 | snprintf(cptr, sizeof(line)-(cptr-line), " %d ", authtype); |
282 | 0 | while ( *cptr ) |
283 | 0 | cptr++; |
284 | |
|
285 | 0 | *cptr++ = ' '; |
286 | 0 | cptr = read_config_save_octet_string(cptr, |
287 | 0 | (u_char *)access_entry->views[authtype], |
288 | 0 | strlen(access_entry->views[authtype]) + 1); |
289 | |
|
290 | 0 | read_config_store(type, line); |
291 | 0 | } |
292 | | |
293 | | char * |
294 | | _vacm_parse_config_access_common(struct vacm_accessEntry **aptr, |
295 | | const char *line) |
296 | 0 | { |
297 | 0 | struct vacm_accessEntry access; |
298 | 0 | char *cPrefix = (char *) &access.contextPrefix; |
299 | 0 | char *gName = (char *) &access.groupName; |
300 | 0 | size_t len; |
301 | |
|
302 | 0 | access.status = atoi(line); |
303 | 0 | line = skip_token_const(line); |
304 | 0 | if (!line) { |
305 | 0 | config_perror("incomplete line"); |
306 | 0 | return NULL; |
307 | 0 | } |
308 | 0 | access.storageType = atoi(line); |
309 | 0 | line = skip_token_const(line); |
310 | 0 | if (!line) { |
311 | 0 | config_perror("incomplete line"); |
312 | 0 | return NULL; |
313 | 0 | } |
314 | 0 | access.securityModel = atoi(line); |
315 | 0 | line = skip_token_const(line); |
316 | 0 | if (!line) { |
317 | 0 | config_perror("incomplete line"); |
318 | 0 | return NULL; |
319 | 0 | } |
320 | 0 | access.securityLevel = atoi(line); |
321 | 0 | line = skip_token_const(line); |
322 | 0 | if (!line) { |
323 | 0 | config_perror("incomplete line"); |
324 | 0 | return NULL; |
325 | 0 | } |
326 | 0 | access.contextMatch = atoi(line); |
327 | 0 | line = skip_token_const(line); |
328 | 0 | len = sizeof(access.groupName); |
329 | 0 | line = read_config_read_octet_string(line, (u_char **) &gName, &len); |
330 | 0 | len = sizeof(access.contextPrefix); |
331 | 0 | line = read_config_read_octet_string(line, (u_char **) &cPrefix, &len); |
332 | |
|
333 | 0 | *aptr = vacm_getAccessEntry(access.groupName, |
334 | 0 | access.contextPrefix, |
335 | 0 | access.securityModel, |
336 | 0 | access.securityLevel); |
337 | 0 | if (!*aptr) |
338 | 0 | *aptr = vacm_createAccessEntry(access.groupName, |
339 | 0 | access.contextPrefix, |
340 | 0 | access.securityModel, |
341 | 0 | access.securityLevel); |
342 | 0 | if (!*aptr) |
343 | 0 | return NULL; |
344 | | |
345 | 0 | (*aptr)->status = access.status; |
346 | 0 | (*aptr)->storageType = access.storageType; |
347 | 0 | (*aptr)->securityModel = access.securityModel; |
348 | 0 | (*aptr)->securityLevel = access.securityLevel; |
349 | 0 | (*aptr)->contextMatch = access.contextMatch; |
350 | 0 | return NETSNMP_REMOVE_CONST(char *, line); |
351 | 0 | } |
352 | | |
353 | | void |
354 | | vacm_parse_config_access(const char *token, const char *line) |
355 | 0 | { |
356 | 0 | struct vacm_accessEntry *aptr; |
357 | 0 | char *readView, *writeView, *notifyView; |
358 | 0 | size_t len; |
359 | |
|
360 | 0 | line = _vacm_parse_config_access_common(&aptr, line); |
361 | 0 | if (!line) |
362 | 0 | return; |
363 | | |
364 | 0 | readView = (char *) aptr->views[VACM_VIEW_READ]; |
365 | 0 | len = sizeof(aptr->views[VACM_VIEW_READ]); |
366 | 0 | line = |
367 | 0 | read_config_read_octet_string(line, (u_char **) & readView, &len); |
368 | 0 | writeView = (char *) aptr->views[VACM_VIEW_WRITE]; |
369 | 0 | len = sizeof(aptr->views[VACM_VIEW_WRITE]); |
370 | 0 | line = |
371 | 0 | read_config_read_octet_string(line, (u_char **) & writeView, &len); |
372 | 0 | notifyView = (char *) aptr->views[VACM_VIEW_NOTIFY]; |
373 | 0 | len = sizeof(aptr->views[VACM_VIEW_NOTIFY]); |
374 | 0 | line = |
375 | 0 | read_config_read_octet_string(line, (u_char **) & notifyView, |
376 | 0 | &len); |
377 | 0 | } |
378 | | |
379 | | void |
380 | | vacm_parse_config_auth_access(const char *token, const char *line) |
381 | 0 | { |
382 | 0 | struct vacm_accessEntry *aptr; |
383 | 0 | int authtype; |
384 | 0 | char *view; |
385 | 0 | size_t len; |
386 | |
|
387 | 0 | line = _vacm_parse_config_access_common(&aptr, line); |
388 | 0 | if (!line) |
389 | 0 | return; |
390 | | |
391 | 0 | authtype = atoi(line); |
392 | 0 | if (authtype < 0 || authtype >= VACM_MAX_VIEWS) { |
393 | 0 | config_perror("invalid authtype"); |
394 | 0 | return; |
395 | 0 | } |
396 | 0 | line = skip_token_const(line); |
397 | |
|
398 | 0 | view = (char *) aptr->views[authtype]; |
399 | 0 | len = sizeof(aptr->views[authtype]); |
400 | 0 | line = read_config_read_octet_string(line, (u_char **) & view, &len); |
401 | 0 | } |
402 | | |
403 | | /* |
404 | | * vacm_save_group(): saves a group entry to the persistent cache |
405 | | */ |
406 | | void |
407 | | vacm_save_group(struct vacm_groupEntry *group_entry, const char *token, |
408 | | const char *type) |
409 | 0 | { |
410 | 0 | char line[4096]; |
411 | 0 | char *cptr; |
412 | |
|
413 | 0 | memset(line, 0, sizeof(line)); |
414 | 0 | snprintf(line, sizeof(line), "%s%s %d %d %d ", |
415 | 0 | token, "Group", group_entry->status, |
416 | 0 | group_entry->storageType, group_entry->securityModel); |
417 | 0 | line[ sizeof(line)-1 ] = 0; |
418 | 0 | cptr = &line[strlen(line)]; /* the NULL */ |
419 | |
|
420 | 0 | cptr = |
421 | 0 | read_config_save_octet_string(cptr, |
422 | 0 | (u_char *) group_entry->securityName + 1, |
423 | 0 | group_entry->securityName[0] + 1); |
424 | 0 | *cptr++ = ' '; |
425 | 0 | cptr = read_config_save_octet_string(cptr, (u_char *) group_entry->groupName, |
426 | 0 | strlen(group_entry->groupName) + 1); |
427 | |
|
428 | 0 | read_config_store(type, line); |
429 | 0 | } |
430 | | |
431 | | void |
432 | | vacm_parse_config_group(const char *token, const char *line) |
433 | 0 | { |
434 | 0 | struct vacm_groupEntry group; |
435 | 0 | struct vacm_groupEntry *gptr; |
436 | 0 | char *securityName = (char *) &group.securityName; |
437 | 0 | char *groupName; |
438 | 0 | size_t len; |
439 | |
|
440 | 0 | group.status = atoi(line); |
441 | 0 | line = skip_token_const(line); |
442 | 0 | if (!line) { |
443 | 0 | config_perror("incomplete line"); |
444 | 0 | return; |
445 | 0 | } |
446 | 0 | group.storageType = atoi(line); |
447 | 0 | line = skip_token_const(line); |
448 | 0 | if (!line) { |
449 | 0 | config_perror("incomplete line"); |
450 | 0 | return; |
451 | 0 | } |
452 | 0 | group.securityModel = atoi(line); |
453 | 0 | line = skip_token_const(line); |
454 | 0 | len = sizeof(group.securityName); |
455 | 0 | line = |
456 | 0 | read_config_read_octet_string(line, (u_char **) & securityName, |
457 | 0 | &len); |
458 | |
|
459 | 0 | gptr = vacm_createGroupEntry(group.securityModel, group.securityName); |
460 | 0 | if (!gptr) |
461 | 0 | return; |
462 | | |
463 | 0 | gptr->status = group.status; |
464 | 0 | gptr->storageType = group.storageType; |
465 | 0 | groupName = (char *) gptr->groupName; |
466 | 0 | len = sizeof(group.groupName); |
467 | 0 | line = |
468 | 0 | read_config_read_octet_string(line, (u_char **) & groupName, &len); |
469 | 0 | } |
470 | | |
471 | | struct vacm_viewEntry * |
472 | | netsnmp_view_get(struct vacm_viewEntry *head, const char *viewName, |
473 | | oid * viewSubtree, size_t viewSubtreeLen, int mode) |
474 | 0 | { |
475 | 0 | struct vacm_viewEntry *vp, *vpret = NULL; |
476 | 0 | char view[VACMSTRINGLEN]; |
477 | 0 | int found, glen; |
478 | 0 | int count=0; |
479 | |
|
480 | 0 | glen = (int) strlen(viewName); |
481 | 0 | if (glen < 0 || glen > VACM_MAX_STRING) |
482 | 0 | return NULL; |
483 | 0 | view[0] = glen; |
484 | 0 | strlcpy(view + 1, viewName, sizeof(view) - 1); |
485 | 0 | for (vp = head; vp; vp = vp->next) { |
486 | 0 | if (!memcmp(view, vp->viewName, glen + 1) |
487 | 0 | && viewSubtreeLen >= (vp->viewSubtreeLen - 1)) { |
488 | 0 | int mask = 0x80; |
489 | 0 | unsigned int oidpos, maskpos = 0; |
490 | 0 | found = 1; |
491 | |
|
492 | 0 | for (oidpos = 0; |
493 | 0 | found && oidpos < vp->viewSubtreeLen - 1; |
494 | 0 | oidpos++) { |
495 | 0 | if (mode==VACM_MODE_IGNORE_MASK || (VIEW_MASK(vp, maskpos, mask) != 0)) { |
496 | 0 | if (viewSubtree[oidpos] != |
497 | 0 | vp->viewSubtree[oidpos + 1]) |
498 | 0 | found = 0; |
499 | 0 | } |
500 | 0 | if (mask == 1) { |
501 | 0 | mask = 0x80; |
502 | 0 | maskpos++; |
503 | 0 | } else |
504 | 0 | mask >>= 1; |
505 | 0 | } |
506 | |
|
507 | 0 | if (found) { |
508 | | /* |
509 | | * match successful, keep this node if its longer than |
510 | | * the previous or (equal and lexicographically greater |
511 | | * than the previous). |
512 | | */ |
513 | 0 | count++; |
514 | 0 | if (mode == VACM_MODE_CHECK_SUBTREE) { |
515 | 0 | vpret = vp; |
516 | 0 | } else if (vpret == NULL |
517 | 0 | || vp->viewSubtreeLen > vpret->viewSubtreeLen |
518 | 0 | || (vp->viewSubtreeLen == vpret->viewSubtreeLen |
519 | 0 | && snmp_oid_compare(vp->viewSubtree + 1, |
520 | 0 | vp->viewSubtreeLen - 1, |
521 | 0 | vpret->viewSubtree + 1, |
522 | 0 | vpret->viewSubtreeLen - 1) > |
523 | 0 | 0)) { |
524 | 0 | vpret = vp; |
525 | 0 | } |
526 | 0 | } |
527 | 0 | } |
528 | 0 | } |
529 | 0 | DEBUGMSGTL(("vacm:getView", ", %s\n", (vpret) ? "found" : "none")); |
530 | 0 | if (mode == VACM_MODE_CHECK_SUBTREE && count > 1) { |
531 | 0 | return NULL; |
532 | 0 | } |
533 | 0 | return vpret; |
534 | 0 | } |
535 | | |
536 | | /*******************************************************************o-o****** |
537 | | * netsnmp_view_exists |
538 | | * |
539 | | * Check to see if a view with the given name exists. |
540 | | * |
541 | | * Parameters: |
542 | | * viewName - Name of view to check |
543 | | * |
544 | | * Returns 0 if the view does not exist. Otherwise, it returns the number |
545 | | * of OID rows for the given name. |
546 | | */ |
547 | | int |
548 | | netsnmp_view_exists(struct vacm_viewEntry *head, const char *viewName) |
549 | 0 | { |
550 | 0 | struct vacm_viewEntry *vp; |
551 | 0 | char view[VACMSTRINGLEN]; |
552 | 0 | int len, count = 0; |
553 | |
|
554 | 0 | len = (int) strlen(viewName); |
555 | 0 | if (len < 0 || len > VACM_MAX_STRING) |
556 | 0 | return 0; |
557 | 0 | view[0] = len; |
558 | 0 | strcpy(view + 1, viewName); |
559 | 0 | DEBUGMSGTL(("9:vacm:view_exists", "checking %s\n", viewName)); |
560 | 0 | for (vp = head; vp; vp = vp->next) { |
561 | 0 | if (memcmp(view, vp->viewName, len + 1) == 0) |
562 | 0 | ++count; |
563 | 0 | } |
564 | |
|
565 | 0 | return count; |
566 | 0 | } |
567 | | |
568 | | /*******************************************************************o-o****** |
569 | | * vacm_checkSubtree |
570 | | * |
571 | | * Check to see if everything within a subtree is in view, not in view, |
572 | | * or possibly both. |
573 | | * |
574 | | * Parameters: |
575 | | * *viewName - Name of view to check |
576 | | * *viewSubtree - OID of subtree |
577 | | * viewSubtreeLen - length of subtree OID |
578 | | * |
579 | | * Returns: |
580 | | * VACM_SUCCESS The OID is included in the view. |
581 | | * VACM_NOTINVIEW If no entry in the view list includes the |
582 | | * provided OID, or the OID is explicitly excluded |
583 | | * from the view. |
584 | | * VACM_SUBTREE_UNKNOWN The entire subtree has both allowed and disallowed |
585 | | * portions. |
586 | | */ |
587 | | int |
588 | | netsnmp_view_subtree_check(struct vacm_viewEntry *head, const char *viewName, |
589 | | oid * viewSubtree, size_t viewSubtreeLen) |
590 | 0 | { |
591 | 0 | struct vacm_viewEntry *vp, *vpShorter = NULL, *vpLonger = NULL; |
592 | 0 | char view[VACMSTRINGLEN]; |
593 | 0 | int found, glen; |
594 | |
|
595 | 0 | glen = (int) strlen(viewName); |
596 | 0 | if (glen < 0 || glen > VACM_MAX_STRING) |
597 | 0 | return VACM_NOTINVIEW; |
598 | 0 | view[0] = glen; |
599 | 0 | strlcpy(view + 1, viewName, sizeof(view) - 1); |
600 | 0 | DEBUGMSGTL(("9:vacm:checkSubtree", "view %s\n", viewName)); |
601 | 0 | for (vp = head; vp; vp = vp->next) { |
602 | 0 | if (!memcmp(view, vp->viewName, glen + 1)) { |
603 | | /* |
604 | | * If the subtree defined in the view is shorter than or equal |
605 | | * to the subtree we are comparing, then it might envelop the |
606 | | * subtree we are comparing against. |
607 | | */ |
608 | 0 | if (viewSubtreeLen >= (vp->viewSubtreeLen - 1)) { |
609 | 0 | int mask = 0x80; |
610 | 0 | unsigned int oidpos, maskpos = 0; |
611 | 0 | found = 1; |
612 | | |
613 | | /* |
614 | | * check the mask |
615 | | */ |
616 | 0 | for (oidpos = 0; |
617 | 0 | found && oidpos < vp->viewSubtreeLen - 1; |
618 | 0 | oidpos++) { |
619 | 0 | if (VIEW_MASK(vp, maskpos, mask) != 0) { |
620 | 0 | if (viewSubtree[oidpos] != |
621 | 0 | vp->viewSubtree[oidpos + 1]) |
622 | 0 | found = 0; |
623 | 0 | } |
624 | 0 | if (mask == 1) { |
625 | 0 | mask = 0x80; |
626 | 0 | maskpos++; |
627 | 0 | } else |
628 | 0 | mask >>= 1; |
629 | 0 | } |
630 | |
|
631 | 0 | if (found) { |
632 | | /* |
633 | | * match successful, keep this node if it's longer than |
634 | | * the previous or (equal and lexicographically greater |
635 | | * than the previous). |
636 | | */ |
637 | 0 | DEBUGMSGTL(("9:vacm:checkSubtree", " %s matched?\n", vp->viewName)); |
638 | | |
639 | 0 | if (vpShorter == NULL |
640 | 0 | || vp->viewSubtreeLen > vpShorter->viewSubtreeLen |
641 | 0 | || (vp->viewSubtreeLen == vpShorter->viewSubtreeLen |
642 | 0 | && snmp_oid_compare(vp->viewSubtree + 1, |
643 | 0 | vp->viewSubtreeLen - 1, |
644 | 0 | vpShorter->viewSubtree + 1, |
645 | 0 | vpShorter->viewSubtreeLen - 1) > |
646 | 0 | 0)) { |
647 | 0 | vpShorter = vp; |
648 | 0 | } |
649 | 0 | } |
650 | 0 | } |
651 | | /* |
652 | | * If the subtree defined in the view is longer than the |
653 | | * subtree we are comparing, then it might ambiguate our |
654 | | * response. |
655 | | */ |
656 | 0 | else { |
657 | 0 | int mask = 0x80; |
658 | 0 | unsigned int oidpos, maskpos = 0; |
659 | 0 | found = 1; |
660 | | |
661 | | /* |
662 | | * check the mask up to the length of the provided subtree |
663 | | */ |
664 | 0 | for (oidpos = 0; |
665 | 0 | found && oidpos < viewSubtreeLen; |
666 | 0 | oidpos++) { |
667 | 0 | if (VIEW_MASK(vp, maskpos, mask) != 0) { |
668 | 0 | if (viewSubtree[oidpos] != |
669 | 0 | vp->viewSubtree[oidpos + 1]) |
670 | 0 | found = 0; |
671 | 0 | } |
672 | 0 | if (mask == 1) { |
673 | 0 | mask = 0x80; |
674 | 0 | maskpos++; |
675 | 0 | } else |
676 | 0 | mask >>= 1; |
677 | 0 | } |
678 | |
|
679 | 0 | if (found) { |
680 | | /* |
681 | | * match successful. If we already found a match |
682 | | * with a different view type, then parts of the subtree |
683 | | * are included and others are excluded, so return UNKNOWN. |
684 | | */ |
685 | 0 | DEBUGMSGTL(("9:vacm:checkSubtree", " %s matched?\n", vp->viewName)); |
686 | 0 | if (vpLonger != NULL |
687 | 0 | && (vpLonger->viewType != vp->viewType)) { |
688 | 0 | DEBUGMSGTL(("vacm:checkSubtree", ", %s\n", "unknown")); |
689 | 0 | return VACM_SUBTREE_UNKNOWN; |
690 | 0 | } |
691 | 0 | else if (vpLonger == NULL) { |
692 | 0 | vpLonger = vp; |
693 | 0 | } |
694 | 0 | } |
695 | 0 | } |
696 | 0 | } |
697 | 0 | } |
698 | 0 | DEBUGMSGTL(("9:vacm:checkSubtree", " %s matched\n", viewName)); |
699 | | |
700 | | /* |
701 | | * If we found a matching view subtree with a longer OID than the provided |
702 | | * OID, check to see if its type is consistent with any matching view |
703 | | * subtree we may have found with a shorter OID than the provided OID. |
704 | | * |
705 | | * The view type of the longer OID is inconsistent with the shorter OID in |
706 | | * either of these two cases: |
707 | | * 1) No matching shorter OID was found and the view type of the longer |
708 | | * OID is INCLUDE. |
709 | | * 2) A matching shorter ID was found and its view type doesn't match |
710 | | * the view type of the longer OID. |
711 | | */ |
712 | 0 | if (vpLonger != NULL) { |
713 | 0 | if ((!vpShorter && vpLonger->viewType != SNMP_VIEW_EXCLUDED) |
714 | 0 | || (vpShorter && vpLonger->viewType != vpShorter->viewType)) { |
715 | 0 | DEBUGMSGTL(("vacm:checkSubtree", ", %s\n", "unknown")); |
716 | 0 | return VACM_SUBTREE_UNKNOWN; |
717 | 0 | } |
718 | 0 | } |
719 | | |
720 | 0 | if (vpShorter && vpShorter->viewType != SNMP_VIEW_EXCLUDED) { |
721 | 0 | DEBUGMSGTL(("vacm:checkSubtree", ", %s\n", "included")); |
722 | 0 | return VACM_SUCCESS; |
723 | 0 | } |
724 | | |
725 | 0 | DEBUGMSGTL(("vacm:checkSubtree", ", %s\n", "excluded")); |
726 | 0 | return VACM_NOTINVIEW; |
727 | 0 | } |
728 | | |
729 | | void |
730 | | vacm_scanViewInit(void) |
731 | 0 | { |
732 | 0 | viewScanPtr = viewList; |
733 | 0 | } |
734 | | |
735 | | struct vacm_viewEntry * |
736 | | vacm_scanViewNext(void) |
737 | 0 | { |
738 | 0 | struct vacm_viewEntry *returnval = viewScanPtr; |
739 | 0 | if (viewScanPtr) |
740 | 0 | viewScanPtr = viewScanPtr->next; |
741 | 0 | return returnval; |
742 | 0 | } |
743 | | |
744 | | struct vacm_viewEntry * |
745 | | netsnmp_view_create(struct vacm_viewEntry **head, const char *viewName, |
746 | | oid * viewSubtree, size_t viewSubtreeLen) |
747 | 0 | { |
748 | 0 | struct vacm_viewEntry *vp, *lp, *op = NULL; |
749 | 0 | int cmp, cmp2, glen; |
750 | |
|
751 | 0 | glen = (int) strlen(viewName); |
752 | 0 | if (glen < 0 || glen > VACM_MAX_STRING || viewSubtreeLen > MAX_OID_LEN) |
753 | 0 | return NULL; |
754 | 0 | vp = calloc(1, sizeof(struct vacm_viewEntry)); |
755 | 0 | if (vp == NULL) |
756 | 0 | return NULL; |
757 | 0 | vp->reserved = calloc(1, sizeof(struct vacm_viewEntry)); |
758 | 0 | if (vp->reserved == NULL) { |
759 | 0 | free(vp); |
760 | 0 | return NULL; |
761 | 0 | } |
762 | | |
763 | 0 | vp->viewName[0] = glen; |
764 | 0 | strlcpy(vp->viewName + 1, viewName, sizeof(vp->viewName) - 1); |
765 | 0 | vp->viewSubtree[0] = viewSubtreeLen; |
766 | 0 | memcpy(vp->viewSubtree + 1, viewSubtree, viewSubtreeLen * sizeof(oid)); |
767 | 0 | vp->viewSubtreeLen = viewSubtreeLen + 1; |
768 | |
|
769 | 0 | lp = *head; |
770 | 0 | while (lp) { |
771 | 0 | cmp = memcmp(lp->viewName, vp->viewName, glen + 1); |
772 | 0 | cmp2 = snmp_oid_compare(lp->viewSubtree, lp->viewSubtreeLen, |
773 | 0 | vp->viewSubtree, vp->viewSubtreeLen); |
774 | 0 | if (cmp == 0 && cmp2 > 0) |
775 | 0 | break; |
776 | 0 | if (cmp > 0) |
777 | 0 | break; |
778 | 0 | op = lp; |
779 | 0 | lp = lp->next; |
780 | 0 | } |
781 | 0 | vp->next = lp; |
782 | 0 | if (op) |
783 | 0 | op->next = vp; |
784 | 0 | else |
785 | 0 | *head = vp; |
786 | 0 | return vp; |
787 | 0 | } |
788 | | |
789 | | void |
790 | | netsnmp_view_destroy(struct vacm_viewEntry **head, const char *viewName, |
791 | | oid * viewSubtree, size_t viewSubtreeLen) |
792 | 0 | { |
793 | 0 | struct vacm_viewEntry *vp, *lastvp = NULL; |
794 | |
|
795 | 0 | if ((*head) && !strcmp((*head)->viewName + 1, viewName) |
796 | 0 | && (*head)->viewSubtreeLen == viewSubtreeLen |
797 | 0 | && !memcmp((char *) (*head)->viewSubtree, (char *) viewSubtree, |
798 | 0 | viewSubtreeLen * sizeof(oid))) { |
799 | 0 | vp = (*head); |
800 | 0 | (*head) = (*head)->next; |
801 | 0 | } else { |
802 | 0 | for (vp = (*head); vp; vp = vp->next) { |
803 | 0 | if (!strcmp(vp->viewName + 1, viewName) |
804 | 0 | && vp->viewSubtreeLen == viewSubtreeLen |
805 | 0 | && !memcmp((char *) vp->viewSubtree, (char *) viewSubtree, |
806 | 0 | viewSubtreeLen * sizeof(oid))) |
807 | 0 | break; |
808 | 0 | lastvp = vp; |
809 | 0 | } |
810 | 0 | if (!vp || !lastvp) |
811 | 0 | return; |
812 | 0 | lastvp->next = vp->next; |
813 | 0 | } |
814 | 0 | if (vp->reserved) |
815 | 0 | free(vp->reserved); |
816 | 0 | free(vp); |
817 | 0 | return; |
818 | 0 | } |
819 | | |
820 | | void |
821 | | netsnmp_view_clear(struct vacm_viewEntry **head) |
822 | 0 | { |
823 | 0 | struct vacm_viewEntry *vp; |
824 | 0 | while ((vp = (*head))) { |
825 | 0 | (*head) = vp->next; |
826 | 0 | if (vp->reserved) |
827 | 0 | free(vp->reserved); |
828 | 0 | free(vp); |
829 | 0 | } |
830 | 0 | } |
831 | | |
832 | | struct vacm_groupEntry * |
833 | | vacm_getGroupEntry(int securityModel, const char *securityName) |
834 | 0 | { |
835 | 0 | struct vacm_groupEntry *vp; |
836 | 0 | char secname[VACMSTRINGLEN]; |
837 | 0 | int glen; |
838 | |
|
839 | 0 | glen = (int) strlen(securityName); |
840 | 0 | if (glen < 0 || glen > VACM_MAX_STRING) |
841 | 0 | return NULL; |
842 | 0 | secname[0] = glen; |
843 | 0 | strlcpy(secname + 1, securityName, sizeof(secname) - 1); |
844 | |
|
845 | 0 | for (vp = groupList; vp; vp = vp->next) { |
846 | 0 | if ((securityModel == vp->securityModel |
847 | 0 | || vp->securityModel == SNMP_SEC_MODEL_ANY) |
848 | 0 | && !memcmp(vp->securityName, secname, glen + 1)) |
849 | 0 | return vp; |
850 | 0 | } |
851 | 0 | return NULL; |
852 | 0 | } |
853 | | |
854 | | void |
855 | | vacm_scanGroupInit(void) |
856 | 0 | { |
857 | 0 | groupScanPtr = groupList; |
858 | 0 | } |
859 | | |
860 | | struct vacm_groupEntry * |
861 | | vacm_scanGroupNext(void) |
862 | 0 | { |
863 | 0 | struct vacm_groupEntry *returnval = groupScanPtr; |
864 | 0 | if (groupScanPtr) |
865 | 0 | groupScanPtr = groupScanPtr->next; |
866 | 0 | return returnval; |
867 | 0 | } |
868 | | |
869 | | struct vacm_groupEntry * |
870 | | vacm_createGroupEntry(int securityModel, const char *securityName) |
871 | 0 | { |
872 | 0 | struct vacm_groupEntry *gp, *lg, *og; |
873 | 0 | int cmp, glen; |
874 | |
|
875 | 0 | glen = (int) strlen(securityName); |
876 | 0 | if (glen < 0 || glen > VACM_MAX_STRING) |
877 | 0 | return NULL; |
878 | 0 | gp = calloc(1, sizeof(struct vacm_groupEntry)); |
879 | 0 | if (gp == NULL) |
880 | 0 | return NULL; |
881 | 0 | gp->reserved = calloc(1, sizeof(struct vacm_groupEntry)); |
882 | 0 | if (gp->reserved == NULL) { |
883 | 0 | free(gp); |
884 | 0 | return NULL; |
885 | 0 | } |
886 | | |
887 | 0 | gp->securityModel = securityModel; |
888 | 0 | gp->securityName[0] = glen; |
889 | 0 | strlcpy(gp->securityName + 1, securityName, sizeof(gp->securityName) - 1); |
890 | |
|
891 | 0 | lg = groupList; |
892 | 0 | og = NULL; |
893 | 0 | while (lg) { |
894 | 0 | if (lg->securityModel > securityModel) |
895 | 0 | break; |
896 | 0 | if (lg->securityModel == securityModel && |
897 | 0 | (cmp = |
898 | 0 | memcmp(lg->securityName, gp->securityName, glen + 1)) > 0) |
899 | 0 | break; |
900 | | /* |
901 | | * if (lg->securityModel == securityModel && cmp == 0) abort(); |
902 | | */ |
903 | 0 | og = lg; |
904 | 0 | lg = lg->next; |
905 | 0 | } |
906 | 0 | gp->next = lg; |
907 | 0 | if (og == NULL) |
908 | 0 | groupList = gp; |
909 | 0 | else |
910 | 0 | og->next = gp; |
911 | 0 | return gp; |
912 | 0 | } |
913 | | |
914 | | void |
915 | | vacm_destroyGroupEntry(int securityModel, const char *securityName) |
916 | 0 | { |
917 | 0 | struct vacm_groupEntry *vp, *lastvp = NULL; |
918 | |
|
919 | 0 | if (groupList && groupList->securityModel == securityModel |
920 | 0 | && !strcmp(groupList->securityName + 1, securityName)) { |
921 | 0 | vp = groupList; |
922 | 0 | groupList = groupList->next; |
923 | 0 | } else { |
924 | 0 | for (vp = groupList; vp; vp = vp->next) { |
925 | 0 | if (vp->securityModel == securityModel |
926 | 0 | && !strcmp(vp->securityName + 1, securityName)) |
927 | 0 | break; |
928 | 0 | lastvp = vp; |
929 | 0 | } |
930 | 0 | if (!vp || !lastvp) |
931 | 0 | return; |
932 | 0 | lastvp->next = vp->next; |
933 | 0 | } |
934 | 0 | if (vp->reserved) |
935 | 0 | free(vp->reserved); |
936 | 0 | free(vp); |
937 | 0 | return; |
938 | 0 | } |
939 | | |
940 | | |
941 | | void |
942 | | vacm_destroyAllGroupEntries(void) |
943 | 0 | { |
944 | 0 | struct vacm_groupEntry *gp; |
945 | 0 | while ((gp = groupList)) { |
946 | 0 | groupList = gp->next; |
947 | 0 | if (gp->reserved) |
948 | 0 | free(gp->reserved); |
949 | 0 | free(gp); |
950 | 0 | } |
951 | 0 | } |
952 | | |
953 | | struct vacm_accessEntry * |
954 | | _vacm_choose_best( struct vacm_accessEntry *current, |
955 | | struct vacm_accessEntry *candidate) |
956 | 0 | { |
957 | | /* |
958 | | * RFC 3415: vacmAccessTable: |
959 | | * 2) if this set has [more than] one member, ... |
960 | | * it comes down to deciding how to weight the |
961 | | * preferences between ContextPrefixes, |
962 | | * SecurityModels, and SecurityLevels |
963 | | */ |
964 | 0 | if (( !current ) || |
965 | | /* a) if the subset of entries with securityModel |
966 | | * matching the securityModel in the message is |
967 | | * not empty, then discard the rest |
968 | | */ |
969 | 0 | ( current->securityModel == SNMP_SEC_MODEL_ANY && |
970 | 0 | candidate->securityModel != SNMP_SEC_MODEL_ANY ) || |
971 | | /* b) if the subset of entries with vacmAccessContextPrefix |
972 | | * matching the contextName in the message is |
973 | | * not empty, then discard the rest |
974 | | */ |
975 | 0 | ( current->contextMatch == CONTEXT_MATCH_PREFIX && |
976 | 0 | candidate->contextMatch == CONTEXT_MATCH_EXACT ) || |
977 | | /* c) discard all entries with ContextPrefixes shorter |
978 | | * than the longest one remaining in the set |
979 | | */ |
980 | 0 | ( current->contextMatch == CONTEXT_MATCH_PREFIX && |
981 | 0 | current->contextPrefix[0] < candidate->contextPrefix[0] ) || |
982 | | /* d) select the entry with the highest securityLevel |
983 | | */ |
984 | 0 | ( current->securityLevel < candidate->securityLevel )) { |
985 | |
|
986 | 0 | return candidate; |
987 | 0 | } |
988 | | |
989 | 0 | return current; |
990 | 0 | } |
991 | | |
992 | | struct vacm_accessEntry * |
993 | | vacm_getAccessEntry(const char *groupName, |
994 | | const char *contextPrefix, |
995 | | int securityModel, int securityLevel) |
996 | 0 | { |
997 | 0 | struct vacm_accessEntry *vp, *best=NULL; |
998 | 0 | char group[VACMSTRINGLEN]; |
999 | 0 | char context[VACMSTRINGLEN]; |
1000 | 0 | int glen, clen; |
1001 | |
|
1002 | 0 | glen = (int) strlen(groupName); |
1003 | 0 | if (glen < 0 || glen > VACM_MAX_STRING) |
1004 | 0 | return NULL; |
1005 | 0 | clen = (int) strlen(contextPrefix); |
1006 | 0 | if (clen < 0 || clen > VACM_MAX_STRING) |
1007 | 0 | return NULL; |
1008 | | |
1009 | 0 | group[0] = glen; |
1010 | 0 | strlcpy(group + 1, groupName, sizeof(group) - 1); |
1011 | 0 | context[0] = clen; |
1012 | 0 | strlcpy(context + 1, contextPrefix, sizeof(context) - 1); |
1013 | 0 | for (vp = accessList; vp; vp = vp->next) { |
1014 | 0 | if ((securityModel == vp->securityModel |
1015 | 0 | || vp->securityModel == SNMP_SEC_MODEL_ANY) |
1016 | 0 | && securityLevel >= vp->securityLevel |
1017 | 0 | && !memcmp(vp->groupName, group, glen + 1) |
1018 | 0 | && |
1019 | 0 | ((vp->contextMatch == CONTEXT_MATCH_EXACT |
1020 | 0 | && clen == vp->contextPrefix[0] |
1021 | 0 | && (memcmp(vp->contextPrefix, context, clen + 1) == 0)) |
1022 | 0 | || (vp->contextMatch == CONTEXT_MATCH_PREFIX |
1023 | 0 | && clen >= vp->contextPrefix[0] |
1024 | 0 | && (memcmp(vp->contextPrefix + 1, context + 1, |
1025 | 0 | vp->contextPrefix[0]) == 0)))) |
1026 | 0 | best = _vacm_choose_best( best, vp ); |
1027 | 0 | } |
1028 | 0 | return best; |
1029 | 0 | } |
1030 | | |
1031 | | void |
1032 | | vacm_scanAccessInit(void) |
1033 | 0 | { |
1034 | 0 | accessScanPtr = accessList; |
1035 | 0 | } |
1036 | | |
1037 | | struct vacm_accessEntry * |
1038 | | vacm_scanAccessNext(void) |
1039 | 0 | { |
1040 | 0 | struct vacm_accessEntry *returnval = accessScanPtr; |
1041 | 0 | if (accessScanPtr) |
1042 | 0 | accessScanPtr = accessScanPtr->next; |
1043 | 0 | return returnval; |
1044 | 0 | } |
1045 | | |
1046 | | struct vacm_accessEntry * |
1047 | | vacm_createAccessEntry(const char *groupName, |
1048 | | const char *contextPrefix, |
1049 | | int securityModel, int securityLevel) |
1050 | 0 | { |
1051 | 0 | struct vacm_accessEntry *vp, *lp, *op = NULL; |
1052 | 0 | int cmp, glen, clen; |
1053 | |
|
1054 | 0 | glen = (int) strlen(groupName); |
1055 | 0 | if (glen < 0 || glen > VACM_MAX_STRING) |
1056 | 0 | return NULL; |
1057 | 0 | clen = (int) strlen(contextPrefix); |
1058 | 0 | if (clen < 0 || clen > VACM_MAX_STRING) |
1059 | 0 | return NULL; |
1060 | 0 | vp = calloc(1, sizeof(struct vacm_accessEntry)); |
1061 | 0 | if (vp == NULL) |
1062 | 0 | return NULL; |
1063 | 0 | vp->reserved = calloc(1, sizeof(struct vacm_accessEntry)); |
1064 | 0 | if (vp->reserved == NULL) { |
1065 | 0 | free(vp); |
1066 | 0 | return NULL; |
1067 | 0 | } |
1068 | | |
1069 | 0 | vp->securityModel = securityModel; |
1070 | 0 | vp->securityLevel = securityLevel; |
1071 | 0 | vp->groupName[0] = glen; |
1072 | 0 | strlcpy(vp->groupName + 1, groupName, sizeof(vp->groupName) - 1); |
1073 | 0 | vp->contextPrefix[0] = clen; |
1074 | 0 | strlcpy(vp->contextPrefix + 1, contextPrefix, |
1075 | 0 | sizeof(vp->contextPrefix) - 1); |
1076 | |
|
1077 | 0 | lp = accessList; |
1078 | 0 | while (lp) { |
1079 | 0 | cmp = memcmp(lp->groupName, vp->groupName, glen + 1); |
1080 | 0 | if (cmp > 0) |
1081 | 0 | break; |
1082 | 0 | if (cmp < 0) |
1083 | 0 | goto next; |
1084 | 0 | cmp = memcmp(lp->contextPrefix, vp->contextPrefix, clen + 1); |
1085 | 0 | if (cmp > 0) |
1086 | 0 | break; |
1087 | 0 | if (cmp < 0) |
1088 | 0 | goto next; |
1089 | 0 | if (lp->securityModel > securityModel) |
1090 | 0 | break; |
1091 | 0 | if (lp->securityModel < securityModel) |
1092 | 0 | goto next; |
1093 | 0 | if (lp->securityLevel > securityLevel) |
1094 | 0 | break; |
1095 | 0 | next: |
1096 | 0 | op = lp; |
1097 | 0 | lp = lp->next; |
1098 | 0 | } |
1099 | 0 | vp->next = lp; |
1100 | 0 | if (op == NULL) |
1101 | 0 | accessList = vp; |
1102 | 0 | else |
1103 | 0 | op->next = vp; |
1104 | 0 | return vp; |
1105 | 0 | } |
1106 | | |
1107 | | void |
1108 | | vacm_destroyAccessEntry(const char *groupName, |
1109 | | const char *contextPrefix, |
1110 | | int securityModel, int securityLevel) |
1111 | 0 | { |
1112 | 0 | struct vacm_accessEntry *vp, *lastvp = NULL; |
1113 | |
|
1114 | 0 | if (accessList && accessList->securityModel == securityModel |
1115 | 0 | && accessList->securityLevel == securityLevel |
1116 | 0 | && !strcmp(accessList->groupName + 1, groupName) |
1117 | 0 | && !strcmp(accessList->contextPrefix + 1, contextPrefix)) { |
1118 | 0 | vp = accessList; |
1119 | 0 | accessList = accessList->next; |
1120 | 0 | } else { |
1121 | 0 | for (vp = accessList; vp; vp = vp->next) { |
1122 | 0 | if (vp->securityModel == securityModel |
1123 | 0 | && vp->securityLevel == securityLevel |
1124 | 0 | && !strcmp(vp->groupName + 1, groupName) |
1125 | 0 | && !strcmp(vp->contextPrefix + 1, contextPrefix)) |
1126 | 0 | break; |
1127 | 0 | lastvp = vp; |
1128 | 0 | } |
1129 | 0 | if (!vp || !lastvp) |
1130 | 0 | return; |
1131 | 0 | lastvp->next = vp->next; |
1132 | 0 | } |
1133 | 0 | if (vp->reserved) |
1134 | 0 | free(vp->reserved); |
1135 | 0 | free(vp); |
1136 | 0 | return; |
1137 | 0 | } |
1138 | | |
1139 | | void |
1140 | | vacm_destroyAllAccessEntries(void) |
1141 | 0 | { |
1142 | 0 | struct vacm_accessEntry *ap; |
1143 | 0 | while ((ap = accessList)) { |
1144 | 0 | accessList = ap->next; |
1145 | 0 | if (ap->reserved) |
1146 | 0 | free(ap->reserved); |
1147 | 0 | free(ap); |
1148 | 0 | } |
1149 | 0 | } |
1150 | | |
1151 | | int |
1152 | | store_vacm(int majorID, int minorID, void *serverarg, void *clientarg) |
1153 | 0 | { |
1154 | | /* |
1155 | | * figure out our application name |
1156 | | */ |
1157 | 0 | char *appname = (char *) clientarg; |
1158 | 0 | if (appname == NULL) { |
1159 | 0 | appname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, |
1160 | 0 | NETSNMP_DS_LIB_APPTYPE); |
1161 | 0 | } |
1162 | | |
1163 | | /* |
1164 | | * save the VACM MIB |
1165 | | */ |
1166 | 0 | vacm_save("vacm", appname); |
1167 | 0 | return SNMPERR_SUCCESS; |
1168 | 0 | } |
1169 | | |
1170 | | /* |
1171 | | * returns 1 if vacm has *any* (non-built-in) configuration entries, |
1172 | | * regardless of whether or not there is enough to make a decision, |
1173 | | * else return 0 |
1174 | | */ |
1175 | | int |
1176 | | vacm_is_configured(void) |
1177 | 0 | { |
1178 | 0 | if (accessList == NULL && groupList == NULL) { |
1179 | 0 | return 0; |
1180 | 0 | } |
1181 | 0 | return 1; |
1182 | 0 | } |
1183 | | |
1184 | | /* |
1185 | | * backwards compatability |
1186 | | */ |
1187 | | struct vacm_viewEntry * |
1188 | | vacm_getViewEntry(const char *viewName, |
1189 | | oid * viewSubtree, size_t viewSubtreeLen, int mode) |
1190 | 0 | { |
1191 | 0 | return netsnmp_view_get( viewList, viewName, viewSubtree, viewSubtreeLen, |
1192 | 0 | mode); |
1193 | 0 | } |
1194 | | |
1195 | | int |
1196 | | vacm_checkSubtree(const char *viewName, |
1197 | | oid * viewSubtree, size_t viewSubtreeLen) |
1198 | 0 | { |
1199 | 0 | return netsnmp_view_subtree_check( viewList, viewName, viewSubtree, |
1200 | 0 | viewSubtreeLen); |
1201 | 0 | } |
1202 | | |
1203 | | struct vacm_viewEntry * |
1204 | | vacm_createViewEntry(const char *viewName, |
1205 | | oid * viewSubtree, size_t viewSubtreeLen) |
1206 | 0 | { |
1207 | 0 | return netsnmp_view_create( &viewList, viewName, viewSubtree, |
1208 | 0 | viewSubtreeLen); |
1209 | 0 | } |
1210 | | |
1211 | | void |
1212 | | vacm_destroyViewEntry(const char *viewName, |
1213 | | oid * viewSubtree, size_t viewSubtreeLen) |
1214 | 0 | { |
1215 | 0 | netsnmp_view_destroy( &viewList, viewName, viewSubtree, viewSubtreeLen); |
1216 | 0 | } |
1217 | | |
1218 | | void |
1219 | | vacm_destroyAllViewEntries(void) |
1220 | 0 | { |
1221 | 0 | netsnmp_view_clear( &viewList ); |
1222 | 0 | } |
1223 | | |
1224 | | /* |
1225 | | * vacm simple api |
1226 | | */ |
1227 | | |
1228 | | int |
1229 | | netsnmp_vacm_simple_usm_add(const char *user, int rw, int authLevel, |
1230 | | const char *view, oid *oidView, size_t oidViewLen, |
1231 | | const char *context) |
1232 | 0 | { |
1233 | 0 | struct vacm_viewEntry *vacmEntry = NULL; |
1234 | 0 | struct vacm_groupEntry *groupEntry = NULL; |
1235 | 0 | struct vacm_accessEntry *accessEntry = NULL; |
1236 | 0 | char *tmp, localContext[VACMSTRINGLEN]; |
1237 | 0 | int exact = 1; /* exact context match */ |
1238 | |
|
1239 | 0 | if (NULL == user) |
1240 | 0 | return SNMPERR_GENERR; |
1241 | | |
1242 | 0 | if (authLevel < SNMP_SEC_LEVEL_NOAUTH || |
1243 | 0 | authLevel > SNMP_SEC_LEVEL_AUTHPRIV) |
1244 | 0 | return SNMPERR_GENERR; |
1245 | | |
1246 | 0 | if (NULL != view) { |
1247 | | /* |
1248 | | * if we are given a view name, it is an error if |
1249 | | * - it exists and we have an oid |
1250 | | * - it doesn't exist and we don't have an oid |
1251 | | */ |
1252 | 0 | if (netsnmp_view_exists(viewList, view) != 0) { |
1253 | 0 | if (NULL != oidView || oidViewLen > 0) { |
1254 | 0 | DEBUGMSGTL(("vacm:simple_usm", "can't modify existing view")); |
1255 | 0 | return SNMPERR_GENERR; |
1256 | 0 | } |
1257 | 0 | } else { |
1258 | 0 | if (NULL == oidView || oidViewLen == 0) { |
1259 | 0 | DEBUGMSGTL(("vacm:simple_usm", "can't create view w/out oid")); |
1260 | 0 | return SNMPERR_GENERR; |
1261 | 0 | } |
1262 | | /** try and create view for oid */ |
1263 | 0 | vacmEntry = vacm_createViewEntry(view, oidView, oidViewLen); |
1264 | 0 | if (NULL == vacmEntry) { |
1265 | 0 | DEBUGMSGTL(("vacm:simple_usm", "createViewEntry failed")); |
1266 | 0 | return SNMPERR_GENERR; |
1267 | 0 | } |
1268 | 0 | SNMP_FREE(vacmEntry->reserved); |
1269 | 0 | } |
1270 | 0 | } else if (0 == oidViewLen || NULL == oidView) { |
1271 | 0 | view = "_all_"; /* no oid either, just use _all_ */ |
1272 | 0 | } else { |
1273 | 0 | DEBUGMSGTL(("vacm:simple_usm", "need view name for new views")); |
1274 | 0 | return SNMPERR_GENERR; |
1275 | 0 | } |
1276 | | |
1277 | | /* |
1278 | | * group |
1279 | | * grpv3user usm \"v3user\"\000prefix\000_all_\000_all_\000_all_\000\060" |
1280 | | * vacm_createGroupEntry() also automatically inserts into group list. |
1281 | | */ |
1282 | 0 | groupEntry = vacm_createGroupEntry(SNMP_SEC_MODEL_USM, user); |
1283 | 0 | if (NULL == groupEntry) { |
1284 | 0 | DEBUGMSGTL(("vacm:simple_usm", "createViewEntry failed")); |
1285 | 0 | goto bail; |
1286 | 0 | } |
1287 | 0 | snprintf(groupEntry->groupName, sizeof(groupEntry->groupName)-2, |
1288 | 0 | "grp%.28s", user); |
1289 | 0 | for (tmp=groupEntry->groupName; *tmp; tmp++) |
1290 | 0 | if (!isalnum((unsigned char)(*tmp))) |
1291 | 0 | *tmp = '_'; |
1292 | 0 | groupEntry->storageType = SNMP_STORAGE_PERMANENT; |
1293 | 0 | groupEntry->status = SNMP_ROW_ACTIVE; |
1294 | 0 | SNMP_FREE(groupEntry->reserved); |
1295 | | |
1296 | | /* |
1297 | | * access |
1298 | | * grpv3user myctx usm noauth exact _all_ none none |
1299 | | */ |
1300 | 0 | if (NULL == context) { |
1301 | 0 | localContext[0] = 0; |
1302 | 0 | context = localContext; |
1303 | 0 | } else { |
1304 | | /** check for wildcard in context */ |
1305 | 0 | int contextLen = strlen(context); |
1306 | 0 | if ('*' == context[contextLen - 1]) { |
1307 | 0 | strlcpy(localContext, context, sizeof(localContext)); |
1308 | 0 | localContext[contextLen - 1] = 0; |
1309 | 0 | context = localContext; |
1310 | 0 | exact = 2; /* not exact, have context prefix */ |
1311 | 0 | } |
1312 | 0 | } |
1313 | 0 | accessEntry = vacm_createAccessEntry(groupEntry->groupName, context, |
1314 | 0 | SNMP_SEC_MODEL_USM, authLevel); |
1315 | 0 | if (NULL == accessEntry) { |
1316 | 0 | DEBUGMSGTL(("vacm:simple_usm", "createViewEntry failed")); |
1317 | 0 | goto bail; |
1318 | 0 | } |
1319 | 0 | strlcpy(accessEntry->views[VACM_VIEW_READ], view, |
1320 | 0 | sizeof(accessEntry->views[VACM_VIEW_READ])); |
1321 | 0 | if (0 == rw) |
1322 | 0 | view = "none"; |
1323 | 0 | strlcpy(accessEntry->views[VACM_VIEW_WRITE], view, |
1324 | 0 | sizeof(accessEntry->views[VACM_VIEW_WRITE])); |
1325 | 0 | strlcpy(accessEntry->views[VACM_VIEW_NOTIFY], view, |
1326 | 0 | sizeof(accessEntry->views[VACM_VIEW_NOTIFY])); |
1327 | |
|
1328 | 0 | accessEntry->contextMatch = exact; |
1329 | 0 | accessEntry->storageType = SNMP_STORAGE_PERMANENT; |
1330 | 0 | accessEntry->status = SNMP_ROW_ACTIVE; |
1331 | 0 | SNMP_FREE(accessEntry->reserved); |
1332 | |
|
1333 | 0 | return SNMPERR_SUCCESS; |
1334 | | |
1335 | 0 | bail: |
1336 | 0 | if (NULL != groupEntry) |
1337 | 0 | vacm_destroyGroupEntry(SNMP_SEC_MODEL_USM, user); |
1338 | |
|
1339 | 0 | if (NULL != vacmEntry) |
1340 | 0 | vacm_destroyViewEntry(vacmEntry->viewName+1, vacmEntry->viewSubtree, |
1341 | 0 | vacmEntry->viewSubtreeLen); |
1342 | |
|
1343 | 0 | return SNMPERR_GENERR; |
1344 | 0 | } |
1345 | | |
1346 | | int |
1347 | | netsnmp_vacm_simple_usm_del(const char *user, int authLevel, |
1348 | | const char *view, oid *oidView, size_t oidViewLen, |
1349 | | const char *context) |
1350 | 0 | { |
1351 | 0 | char localContext[VACMSTRINGLEN]; |
1352 | 0 | char group[VACMSTRINGLEN]; |
1353 | | |
1354 | | /* |
1355 | | * only delete simple views (one OID) for which we have an OID. |
1356 | | * never delete '_all_'. |
1357 | | */ |
1358 | 0 | if ((NULL != view) && (NULL != oidView) && (oidViewLen > 0) && |
1359 | 0 | (strcmp(view, "_all_") != 0) && |
1360 | 0 | (netsnmp_view_exists(viewList, view) == 1)) { |
1361 | 0 | vacm_destroyViewEntry(view, oidView, oidViewLen); |
1362 | 0 | } |
1363 | |
|
1364 | 0 | vacm_destroyGroupEntry(SNMP_SEC_MODEL_USM, user); |
1365 | |
|
1366 | 0 | snprintf(group, sizeof(group)-2, "grp%.28s", user); |
1367 | 0 | if (NULL == context) { |
1368 | 0 | localContext[0] = 0; |
1369 | 0 | context = localContext; |
1370 | 0 | } else { |
1371 | | /** check for wildcard in context */ |
1372 | 0 | int contextLen = strlen(context); |
1373 | 0 | if ('*' == context[contextLen - 1]) { |
1374 | 0 | strlcpy(localContext, context, sizeof(localContext)); |
1375 | 0 | localContext[contextLen - 1] = 0; |
1376 | 0 | context = localContext; |
1377 | 0 | } |
1378 | 0 | } |
1379 | |
|
1380 | 0 | vacm_destroyAccessEntry(group, context, SNMP_SEC_MODEL_USM, authLevel); |
1381 | |
|
1382 | 0 | return SNMPERR_SUCCESS; |
1383 | 0 | } |