added files
[bcm963xx.git] / userapps / opensource / net-snmp / agent / mibgroup / snmpv3 / usmUser.c
1 /*
2  * usmUser.c
3  */
4
5 #include <net-snmp/net-snmp-config.h>
6 #include <stdlib.h>
7
8 #if HAVE_STRING_H
9 #include <string.h>
10 #else
11 #include <strings.h>
12 #endif
13
14 #if HAVE_WINSOCK_H
15 #include <winsock.h>
16 #endif
17
18 #include <net-snmp/net-snmp-includes.h>
19 #include <net-snmp/agent/net-snmp-agent-includes.h>
20
21 #include "util_funcs.h"
22 #include "usmUser.h"
23
24 int usmStatusCheck(struct usmUser *uptr);
25
26 struct variable4 usmUser_variables[] = {
27     {USMUSERSPINLOCK, ASN_INTEGER, RWRITE, var_usmUser, 1, {1}},
28     {USMUSERSECURITYNAME, ASN_OCTET_STR, RONLY, var_usmUser, 3, {2, 1, 3}},
29     {USMUSERCLONEFROM, ASN_OBJECT_ID, RWRITE, var_usmUser, 3, {2, 1, 4}},
30     {USMUSERAUTHPROTOCOL, ASN_OBJECT_ID, RWRITE, var_usmUser, 3,
31      {2, 1, 5}},
32     {USMUSERAUTHKEYCHANGE, ASN_OCTET_STR, RWRITE, var_usmUser, 3,
33      {2, 1, 6}},
34     {USMUSEROWNAUTHKEYCHANGE, ASN_OCTET_STR, RWRITE, var_usmUser, 3,
35      {2, 1, 7}},
36     {USMUSERPRIVPROTOCOL, ASN_OBJECT_ID, RWRITE, var_usmUser, 3,
37      {2, 1, 8}},
38     {USMUSERPRIVKEYCHANGE, ASN_OCTET_STR, RWRITE, var_usmUser, 3,
39      {2, 1, 9}},
40     {USMUSEROWNPRIVKEYCHANGE, ASN_OCTET_STR, RWRITE, var_usmUser, 3,
41      {2, 1, 10}},
42     {USMUSERPUBLIC, ASN_OCTET_STR, RWRITE, var_usmUser, 3, {2, 1, 11}},
43     {USMUSERSTORAGETYPE, ASN_INTEGER, RWRITE, var_usmUser, 3, {2, 1, 12}},
44     {USMUSERSTATUS, ASN_INTEGER, RWRITE, var_usmUser, 3, {2, 1, 13}},
45
46 };
47
48 oid             usmUser_variables_oid[] = { 1, 3, 6, 1, 6, 3, 15, 1, 2 };
49
50
51 /*
52  * needed for the write_ functions to find the start of the index 
53  */
54 #define USM_MIB_LENGTH 12
55
56 static unsigned int usmUserSpinLock = 0;
57
58 void
59 init_usmUser(void)
60 {
61     snmpd_register_config_handler("usmUser",
62                                   usm_parse_config_usmUser, NULL, NULL);
63     snmpd_register_config_handler("createUser",
64                                   usm_parse_create_usmUser, NULL,
65                                   "username (MD5|SHA) passphrase [DES [passphrase]]");
66
67     /*
68      * we need to be called back later 
69      */
70     snmp_register_callback(SNMP_CALLBACK_LIBRARY, SNMP_CALLBACK_STORE_DATA,
71                            usm_store_users, NULL);
72
73     REGISTER_MIB("snmpv3/usmUser", usmUser_variables, variable4,
74                  usmUser_variables_oid);
75 }
76
77 /*******************************************************************-o-******
78  * usm_generate_OID
79  *
80  * Parameters:
81  *      *prefix         (I) OID prefix to the usmUser table entry.
82  *       prefixLen      (I)
83  *      *uptr           (I) Pointer to a user in the user list.
84  *      *length         (O) Length of generated index OID.
85  *      
86  * Returns:
87  *      Pointer to the OID index for the user (uptr)  -OR-
88  *      NULL on failure.
89  *
90  *
91  * Generate the index OID for a given usmUser name.  'length' is set to
92  * the length of the index OID.
93  *
94  * Index OID format is:
95  *
96  *    <...prefix>.<engineID_length>.<engineID>.<user_name_length>.<user_name>
97  */
98 oid            *
99 usm_generate_OID(oid * prefix, size_t prefixLen, struct usmUser *uptr,
100                  size_t * length)
101 {
102     oid            *indexOid;
103     int             i;
104
105     *length = 2 + uptr->engineIDLen + strlen(uptr->name) + prefixLen;
106     indexOid = (oid *) malloc(*length * sizeof(oid));
107     if (indexOid) {
108         memmove(indexOid, prefix, prefixLen * sizeof(oid));
109
110         indexOid[prefixLen] = uptr->engineIDLen;
111         for (i = 0; i < (int) uptr->engineIDLen; i++)
112             indexOid[prefixLen + 1 + i] = (oid) uptr->engineID[i];
113
114         indexOid[prefixLen + uptr->engineIDLen + 1] = strlen(uptr->name);
115         for (i = 0; i < (int) strlen(uptr->name); i++)
116             indexOid[prefixLen + uptr->engineIDLen + 2 + i] =
117                 (oid) uptr->name[i];
118     }
119     return indexOid;
120
121 }                               /* end usm_generate_OID() */
122
123 /*
124  * usm_parse_oid(): parses an index to the usmTable to break it down into
125  * a engineID component and a name component.  The results are stored in:
126  * 
127  * **engineID:   a newly malloced string.
128  * *engineIDLen: The length of the malloced engineID string above.
129  * **name:       a newly malloced string.
130  * *nameLen:     The length of the malloced name string above.
131  * 
132  * returns 1 if an error is encountered, or 0 if successful.
133  */
134 int
135 usm_parse_oid(oid * oidIndex, size_t oidLen,
136               unsigned char **engineID, size_t * engineIDLen,
137               unsigned char **name, size_t * nameLen)
138 {
139     int             nameL;
140     int             engineIDL;
141     int             i;
142
143     /*
144      * first check the validity of the oid 
145      */
146     if ((oidLen <= 0) || (!oidIndex)) {
147         DEBUGMSGTL(("usmUser",
148                     "parse_oid: null oid or zero length oid passed in\n"));
149         return 1;
150     }
151     engineIDL = *oidIndex;      /* initial engineID length */
152     if ((int) oidLen < engineIDL + 2) {
153         DEBUGMSGTL(("usmUser",
154                     "parse_oid: invalid oid length: less than the engineIDLen\n"));
155         return 1;
156     }
157     nameL = oidIndex[engineIDL + 1];    /* the initial name length */
158     if ((int) oidLen != engineIDL + nameL + 2) {
159         DEBUGMSGTL(("usmUser",
160                     "parse_oid: invalid oid length: length is not exact\n"));
161         return 1;
162     }
163
164     /*
165      * its valid, malloc the space and store the results 
166      */
167     if (engineID == NULL || name == NULL) {
168         DEBUGMSGTL(("usmUser",
169                     "parse_oid: null storage pointer passed in.\n"));
170         return 1;
171     }
172
173     *engineID = (unsigned char *) malloc(engineIDL);
174     if (*engineID == NULL) {
175         DEBUGMSGTL(("usmUser",
176                     "parse_oid: malloc of the engineID failed\n"));
177         return 1;
178     }
179     *engineIDLen = engineIDL;
180
181     *name = (unsigned char *) malloc(nameL + 1);
182     if (*name == NULL) {
183         DEBUGMSGTL(("usmUser", "parse_oid: malloc of the name failed\n"));
184         free(*engineID);
185         return 1;
186     }
187     *nameLen = nameL;
188
189     for (i = 0; i < engineIDL; i++) {
190         if (oidIndex[i + 1] > 255) {
191             goto UPO_parse_error;
192         }
193         engineID[0][i] = (unsigned char) oidIndex[i + 1];
194     }
195
196     for (i = 0; i < nameL; i++) {
197         if (oidIndex[i + 2 + engineIDL] > 255) {
198           UPO_parse_error:
199             free(*engineID);
200             free(*name);
201             return 1;
202         }
203         name[0][i] = (unsigned char) oidIndex[i + 2 + engineIDL];
204     }
205     name[0][nameL] = 0;
206
207     return 0;
208
209 }                               /* end usm_parse_oid() */
210
211 /*******************************************************************-o-******
212  * usm_parse_user
213  *
214  * Parameters:
215  *      *name           Complete OID indexing a given usmUser entry.
216  *       name_length
217  *      
218  * Returns:
219  *      Pointer to a usmUser  -OR-
220  *      NULL if name does not convert to a usmUser.
221  * 
222  * Convert an (full) OID and return a pointer to a matching user in the
223  * user list if one exists.
224  */
225 struct usmUser *
226 usm_parse_user(oid * name, size_t name_len)
227 {
228     struct usmUser *uptr;
229
230     char           *newName;
231     u_char         *engineID;
232     size_t          nameLen, engineIDLen;
233
234     /*
235      * get the name and engineID out of the incoming oid 
236      */
237     if (usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH,
238                       &engineID, &engineIDLen, (u_char **) & newName,
239                       &nameLen))
240         return NULL;
241
242     /*
243      * Now see if a user exists with these index values 
244      */
245     uptr = usm_get_user(engineID, engineIDLen, newName);
246     free(engineID);
247     free(newName);
248
249     return uptr;
250
251 }                               /* end usm_parse_user() */
252
253 /*******************************************************************-o-******
254  * var_usmUser
255  *
256  * Parameters:
257  *        *vp      (I)     Variable-binding associated with this action.
258  *        *name    (I/O)   Input name requested, output name found.
259  *        *length  (I/O)   Length of input and output oid's.
260  *         exact   (I)     TRUE if an exact match was requested.
261  *        *var_len (O)     Length of variable or 0 if function returned.
262  *      (**write_method)   Hook to name a write method (UNUSED).
263  *      
264  * Returns:
265  *      Pointer to (char *) containing related data of length 'length'
266  *        (May be NULL.)
267  *
268  *
269  * Call-back function passed to the agent in order to return information
270  * for the USM MIB tree.
271  *
272  *
273  * If this invocation is not for USMUSERSPINLOCK, lookup user name
274  * in the usmUser list.
275  *
276  * If the name does not match any user and the request
277  * is for an exact match, -or- if the usmUser list is empty, create a 
278  * new list entry.
279  *
280  * Finally, service the given USMUSER* var-bind.  A NULL user generally
281  * results in a NULL return value.
282  */
283 u_char         *
284 var_usmUser(struct variable * vp,
285             oid * name,
286             size_t * length,
287             int exact, size_t * var_len, WriteMethod ** write_method)
288 {
289     struct usmUser *uptr = NULL, *nptr, *pptr;
290     int             i, rtest, result;
291     oid            *indexOid;
292     size_t          len;
293
294     /*
295      * variables we may use later 
296      */
297     static long     long_ret;
298     static u_char   string[1];
299     static oid      objid[2];   /* for .0.0 */
300
301     *write_method = 0;          /* assume it isnt writable for the time being */
302     *var_len = sizeof(long_ret);        /* assume an integer and change later if not */
303
304     if (vp->magic != USMUSERSPINLOCK) {
305         oid             newname[MAX_OID_LEN];
306         len = (*length < vp->namelen) ? *length : vp->namelen;
307         rtest = snmp_oid_compare(name, len, vp->name, len);
308         if (rtest > 0 ||
309             /*
310              * (rtest == 0 && !exact && (int) vp->namelen+1 < (int) *length) || 
311              */
312             (exact == 1 && rtest != 0)) {
313             if (var_len)
314                 *var_len = 0;
315             return 0;
316         }
317         memset(newname, 0, sizeof(newname));
318         if (((int) *length) <= (int) vp->namelen || rtest == -1) {
319             /*
320              * oid is not within our range yet 
321              */
322             /*
323              * need to fail if not exact 
324              */
325             uptr = usm_get_userList();
326
327         } else {
328             for (nptr = usm_get_userList(), pptr = NULL, uptr = NULL;
329                  nptr != NULL; pptr = nptr, nptr = nptr->next) {
330                 indexOid =
331                     usm_generate_OID(vp->name, vp->namelen, nptr, &len);
332                 result = snmp_oid_compare(name, *length, indexOid, len);
333                 DEBUGMSGTL(("usmUser", "Checking user: %s - ",
334                             nptr->name));
335                 for (i = 0; i < (int) nptr->engineIDLen; i++) {
336                     DEBUGMSG(("usmUser", " %x", nptr->engineID[i]));
337                 }
338                 DEBUGMSG(("usmUser", " - %d \n  -> OID: ", result));
339                 DEBUGMSGOID(("usmUser", indexOid, len));
340                 DEBUGMSG(("usmUser", "\n"));
341
342                 free(indexOid);
343
344                 if (exact) {
345                     if (result == 0) {
346                         uptr = nptr;
347                     }
348                 } else {
349                     if (result == 0) {
350                         /*
351                          * found an exact match.  Need the next one for !exact 
352                          */
353                         uptr = nptr->next;
354                     } else if (result == -1) {
355                         uptr = nptr;
356                         break;
357                     }
358                 }
359             }
360         }                       /* endif -- name <= vp->name */
361
362         /*
363          * if uptr is NULL and exact we need to continue for creates 
364          */
365         if (uptr == NULL && !exact)
366             return (NULL);
367
368         if (uptr) {
369             indexOid = usm_generate_OID(vp->name, vp->namelen, uptr, &len);
370             *length = len;
371             memmove(name, indexOid, len * sizeof(oid));
372             DEBUGMSGTL(("usmUser", "Found user: %s - ", uptr->name));
373             for (i = 0; i < (int) uptr->engineIDLen; i++) {
374                 DEBUGMSG(("usmUser", " %x", uptr->engineID[i]));
375             }
376             DEBUGMSG(("usmUser", "\n  -> OID: "));
377             DEBUGMSGOID(("usmUser", indexOid, len));
378             DEBUGMSG(("usmUser", "\n"));
379
380             free(indexOid);
381         }
382     } else {
383         if (header_generic(vp, name, length, exact, var_len, write_method))
384             return 0;
385     }                           /* endif -- vp->magic != USMUSERSPINLOCK */
386
387     switch (vp->magic) {
388     case USMUSERSPINLOCK:
389         *write_method = write_usmUserSpinLock;
390         long_ret = usmUserSpinLock;
391         return (unsigned char *) &long_ret;
392
393     case USMUSERSECURITYNAME:
394         if (uptr) {
395             *var_len = strlen(uptr->secName);
396             return (unsigned char *) uptr->secName;
397         }
398         return NULL;
399
400     case USMUSERCLONEFROM:
401         *write_method = write_usmUserCloneFrom;
402         if (uptr) {
403             objid[0] = 0;       /* "When this object is read, the ZeroDotZero OID */
404             objid[1] = 0;       /*  is returned." */
405             *var_len = sizeof(oid) * 2;
406             return (unsigned char *) objid;
407         }
408         return NULL;
409
410     case USMUSERAUTHPROTOCOL:
411         *write_method = write_usmUserAuthProtocol;
412         if (uptr) {
413             *var_len = uptr->authProtocolLen * sizeof(oid);
414             return (u_char *) uptr->authProtocol;
415         }
416         return NULL;
417
418     case USMUSERAUTHKEYCHANGE:
419     case USMUSEROWNAUTHKEYCHANGE:
420         /*
421          * we treat these the same, and let the calling module
422          * distinguish between them 
423          */
424         *write_method = write_usmUserAuthKeyChange;
425         if (uptr) {
426             *string = 0;        /* always return a NULL string */
427             *var_len = 0;
428             return string;
429         }
430         return NULL;
431
432     case USMUSERPRIVPROTOCOL:
433         *write_method = write_usmUserPrivProtocol;
434         if (uptr) {
435             *var_len = uptr->privProtocolLen * sizeof(oid);
436             return (u_char *) uptr->privProtocol;
437         }
438         return NULL;
439
440     case USMUSERPRIVKEYCHANGE:
441     case USMUSEROWNPRIVKEYCHANGE:
442         /*
443          * we treat these the same, and let the calling module
444          * distinguish between them 
445          */
446         *write_method = write_usmUserPrivKeyChange;
447         if (uptr) {
448             *string = 0;        /* always return a NULL string */
449             *var_len = 0;
450             return string;
451         }
452         return NULL;
453
454     case USMUSERPUBLIC:
455         *write_method = write_usmUserPublic;
456         if (uptr) {
457             if (uptr->userPublicString) {
458                 *var_len = strlen((char *) uptr->userPublicString);
459                 return uptr->userPublicString;
460             }
461             *string = 0;
462             *var_len = 0;       /* return an empty string if the public
463                                  * string hasn't been defined yet */
464             return string;
465         }
466         return NULL;
467
468     case USMUSERSTORAGETYPE:
469         *write_method = write_usmUserStorageType;
470         if (uptr) {
471             long_ret = uptr->userStorageType;
472             return (unsigned char *) &long_ret;
473         }
474         return NULL;
475
476     case USMUSERSTATUS:
477         *write_method = write_usmUserStatus;
478         if (uptr) {
479             long_ret = uptr->userStatus;
480             return (unsigned char *) &long_ret;
481         }
482         return NULL;
483
484     default:
485         DEBUGMSGTL(("snmpd", "unknown sub-id %d in var_usmUser\n",
486                     vp->magic));
487     }
488     return 0;
489
490 }                               /* end var_usmUser() */
491
492 /*
493  * write_usmUserSpinLock(): called when a set is performed on the
494  * usmUserSpinLock object 
495  */
496 int
497 write_usmUserSpinLock(int action,
498                       u_char * var_val,
499                       u_char var_val_type,
500                       size_t var_val_len,
501                       u_char * statP, oid * name, size_t name_len)
502 {
503     /*
504      * variables we may use later 
505      */
506     static long     long_ret;
507
508     if (var_val_type != ASN_INTEGER) {
509         DEBUGMSGTL(("usmUser",
510                     "write to usmUserSpinLock not ASN_INTEGER\n"));
511         return SNMP_ERR_WRONGTYPE;
512     }
513     if (var_val_len > sizeof(long_ret)) {
514         DEBUGMSGTL(("usmUser", "write to usmUserSpinLock: bad length\n"));
515         return SNMP_ERR_WRONGLENGTH;
516     }
517     long_ret = *((long *) var_val);
518     if (long_ret != (long) usmUserSpinLock)
519         return SNMP_ERR_INCONSISTENTVALUE;
520     if (action == COMMIT) {
521         if (usmUserSpinLock == 2147483647)
522             usmUserSpinLock = 0;
523         else
524             usmUserSpinLock++;
525     }
526     return SNMP_ERR_NOERROR;
527 }                               /* end write_usmUserSpinLock() */
528
529 /*******************************************************************-o-******
530  * write_usmUserCloneFrom
531  *
532  * Parameters:
533  *       action
534  *      *var_val
535  *       var_val_type
536  *       var_val_len
537  *      *statP          (UNUSED)
538  *      *name           OID of user to clone from.
539  *       name_len
540  *      
541  * Returns:
542  *      SNMP_ERR_NOERROR                On success  -OR-  If user exists
543  *                                        and has already been cloned.
544  *      SNMP_ERR_GENERR                 Local function call failures.
545  *      SNMP_ERR_INCONSISTENTNAME       'name' does not exist in user list
546  *                                        -OR-  user to clone from != RS_ACTIVE.
547  *      SNMP_ERR_WRONGLENGTH            OID length > than local buffer size.
548  *      SNMP_ERR_WRONGTYPE              ASN_OBJECT_ID is wrong.
549  *
550  *
551  * XXX:  should handle action=UNDO's.
552  */
553 int
554 write_usmUserCloneFrom(int action,
555                        u_char * var_val,
556                        u_char var_val_type,
557                        size_t var_val_len,
558                        u_char * statP, oid * name, size_t name_len)
559 {
560     struct usmUser *uptr, *cloneFrom;
561
562     if (action == RESERVE1) {
563         if (var_val_type != ASN_OBJECT_ID) {
564             DEBUGMSGTL(("usmUser",
565                         "write to usmUserCloneFrom not ASN_OBJECT_ID\n"));
566             return SNMP_ERR_WRONGTYPE;
567         }
568         if (var_val_len > USM_LENGTH_OID_MAX * sizeof(oid) ||
569             var_val_len % sizeof(oid) != 0) {
570             DEBUGMSGTL(("usmUser",
571                         "write to usmUserCloneFrom: bad length\n"));
572             return SNMP_ERR_WRONGLENGTH;
573         }
574     } else if (action == RESERVE2) {
575         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
576             /*
577              * We don't allow creations here.  
578              */
579             return SNMP_ERR_INCONSISTENTNAME;
580         }
581
582         /*
583          * Has the user already been cloned?  If so, writes to this variable
584          * are defined to have no effect and to produce no error.  
585          */
586         if (uptr->cloneFrom != NULL) {
587             return SNMP_ERR_NOERROR;
588         }
589
590         cloneFrom =
591             usm_parse_user((oid *) var_val, var_val_len / sizeof(oid));
592         if (cloneFrom == NULL || cloneFrom->userStatus != SNMP_ROW_ACTIVE) {
593             return SNMP_ERR_INCONSISTENTNAME;
594         }
595         uptr->cloneFrom = snmp_duplicate_objid((oid *) var_val,
596                                                var_val_len / sizeof(oid));
597         usm_cloneFrom_user(cloneFrom, uptr);
598
599         if (usmStatusCheck(uptr) && uptr->userStatus == SNMP_ROW_NOTREADY) {
600             uptr->userStatus = SNMP_ROW_NOTINSERVICE;
601         }
602     }
603
604     return SNMP_ERR_NOERROR;
605 }
606
607 /*******************************************************************-o-******
608  * write_usmUserAuthProtocol
609  *
610  * Parameters:
611  *       action
612  *      *var_val        OID of auth transform to set.
613  *       var_val_type
614  *       var_val_len
615  *      *statP
616  *      *name           OID of user upon which to perform set operation.
617  *       name_len
618  *      
619  * Returns:
620  *      SNMP_ERR_NOERROR                On success.
621  *      SNMP_ERR_GENERR
622  *      SNMP_ERR_INCONSISTENTVALUE
623  *      SNMP_ERR_NOSUCHNAME
624  *      SNMP_ERR_WRONGLENGTH
625  *      SNMP_ERR_WRONGTYPE
626  */
627 int
628 write_usmUserAuthProtocol(int action,
629                           u_char * var_val,
630                           u_char var_val_type,
631                           size_t var_val_len,
632                           u_char * statP, oid * name, size_t name_len)
633 {
634     static oid     *optr;
635     static size_t   olen;
636     static int      resetOnFail;
637     struct usmUser *uptr;
638
639     if (action == RESERVE1) {
640         resetOnFail = 0;
641         if (var_val_type != ASN_OBJECT_ID) {
642             DEBUGMSGTL(("usmUser",
643                         "write to usmUserAuthProtocol not ASN_OBJECT_ID\n"));
644             return SNMP_ERR_WRONGTYPE;
645         }
646         if (var_val_len > USM_LENGTH_OID_MAX * sizeof(oid) ||
647             var_val_len % sizeof(oid) != 0) {
648             DEBUGMSGTL(("usmUser",
649                         "write to usmUserAuthProtocol: bad length\n"));
650             return SNMP_ERR_WRONGLENGTH;
651         }
652     } else if (action == RESERVE2) {
653         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
654             return SNMP_ERR_INCONSISTENTNAME;
655         }
656
657         if (uptr->userStatus == RS_ACTIVE
658             || uptr->userStatus == RS_NOTREADY
659             || uptr->userStatus == RS_NOTINSERVICE) {
660             /*
661              * The authProtocol is already set.  It is only legal to CHANGE it
662              * to usmNoAuthProtocol...  
663              */
664             if (snmp_oid_compare
665                 ((oid *) var_val, var_val_len / sizeof(oid),
666                  usmNoAuthProtocol,
667                  sizeof(usmNoAuthProtocol) / sizeof(oid)) == 0) {
668                 /*
669                  * ... and then only if the privProtocol is equal to
670                  * usmNoPrivProtocol.  
671                  */
672                 if (snmp_oid_compare
673                     (uptr->privProtocol, uptr->privProtocolLen,
674                      usmNoPrivProtocol,
675                      sizeof(usmNoPrivProtocol) / sizeof(oid)) != 0) {
676                     return SNMP_ERR_INCONSISTENTVALUE;
677                 }
678                 optr = uptr->authProtocol;
679                 olen = uptr->authProtocolLen;
680                 resetOnFail = 1;
681                 uptr->authProtocol = snmp_duplicate_objid((oid *) var_val,
682                                                           var_val_len /
683                                                           sizeof(oid));
684                 if (uptr->authProtocol == NULL) {
685                     return SNMP_ERR_RESOURCEUNAVAILABLE;
686                 }
687                 uptr->authProtocolLen = var_val_len / sizeof(oid);
688             } else
689                 if (snmp_oid_compare
690                     ((oid *) var_val, var_val_len / sizeof(oid),
691                      uptr->authProtocol, uptr->authProtocolLen) == 0) {
692                 /*
693                  * But it's also okay to set it to the same thing as it
694                  * currently is.  
695                  */
696                 return SNMP_ERR_NOERROR;
697             } else {
698                 return SNMP_ERR_INCONSISTENTVALUE;
699             }
700         } else {
701             /*
702              * This row is under creation.  It's okay to set
703              * usmUserAuthProtocol to any valid authProtocol but it will be
704              * overwritten when usmUserCloneFrom is set (so don't write it if
705              * that has already been set).  
706              */
707
708             if (snmp_oid_compare
709                 ((oid *) var_val, var_val_len / sizeof(oid),
710                  usmNoAuthProtocol,
711                  sizeof(usmNoAuthProtocol) / sizeof(oid)) == 0
712                 || snmp_oid_compare((oid *) var_val,
713                                     var_val_len / sizeof(oid),
714                                     usmHMACMD5AuthProtocol,
715                                     sizeof(usmHMACMD5AuthProtocol) /
716                                     sizeof(oid)) == 0
717                 || snmp_oid_compare((oid *) var_val,
718                                     var_val_len / sizeof(oid),
719                                     usmHMACSHA1AuthProtocol,
720                                     sizeof(usmHMACSHA1AuthProtocol) /
721                                     sizeof(oid)) == 0) {
722                 if (uptr->cloneFrom != NULL) {
723                     optr = uptr->authProtocol;
724                     olen = uptr->authProtocolLen;
725                     resetOnFail = 1;
726                     uptr->authProtocol =
727                         snmp_duplicate_objid((oid *) var_val,
728                                              var_val_len / sizeof(oid));
729                     if (uptr->authProtocol == NULL) {
730                         return SNMP_ERR_RESOURCEUNAVAILABLE;
731                     }
732                     uptr->authProtocolLen = var_val_len / sizeof(oid);
733                 }
734             } else {
735                 /*
736                  * Unknown authentication protocol.  
737                  */
738                 return SNMP_ERR_WRONGVALUE;
739             }
740         }
741     } else if (action == COMMIT) {
742         SNMP_FREE(optr);
743         optr = NULL;
744     } else if (action == FREE || action == UNDO) {
745         if ((uptr = usm_parse_user(name, name_len)) != NULL) {
746             if (resetOnFail) {
747                 SNMP_FREE(uptr->authProtocol);
748                 uptr->authProtocol = optr;
749                 uptr->authProtocolLen = olen;
750             }
751         }
752     }
753     return SNMP_ERR_NOERROR;
754 }                               /* end write_usmUserAuthProtocol() */
755
756 /*******************************************************************-o-******
757  * write_usmUserAuthKeyChange
758  *
759  * Parameters:
760  *       action         
761  *      *var_val        Octet string representing new KeyChange value.
762  *       var_val_type
763  *       var_val_len
764  *      *statP          (UNUSED)
765  *      *name           OID of user upon which to perform set operation.
766  *       name_len
767  *      
768  * Returns:
769  *      SNMP_ERR_NOERR          Success.
770  *      SNMP_ERR_WRONGTYPE      
771  *      SNMP_ERR_WRONGLENGTH    
772  *      SNMP_ERR_NOSUCHNAME     
773  *      SNMP_ERR_GENERR
774  *
775  * Note: This function handles both the usmUserAuthKeyChange and
776  *       usmUserOwnAuthKeyChange objects.  We are not passed the name
777  *       of the user requseting the keychange, so we leave this to the
778  *       calling module to verify when and if we should be called.  To
779  *       change this would require a change in the mib module API to
780  *       pass in the securityName requesting the change.
781  *
782  * XXX:  should handle action=UNDO's.
783  */
784 int
785 write_usmUserAuthKeyChange(int action,
786                            u_char * var_val,
787                            u_char var_val_type,
788                            size_t var_val_len,
789                            u_char * statP, oid * name, size_t name_len)
790 {
791     struct usmUser *uptr;
792     unsigned char   buf[SNMP_MAXBUF_SMALL];
793     size_t          buflen = SNMP_MAXBUF_SMALL;
794     const char      fnAuthKey[] = "write_usmUserAuthKeyChange";
795     const char      fnOwnAuthKey[] = "write_usmUserOwnAuthKeyChange";
796     const char     *fname;
797     static unsigned char *oldkey;
798     static size_t   oldkeylen;
799     static int      resetOnFail;
800
801     if (name[USM_MIB_LENGTH - 1] == 6) {
802         fname = fnAuthKey;
803     } else {
804         fname = fnOwnAuthKey;
805     }
806
807     if (action == RESERVE1) {
808         resetOnFail = 0;
809         if (var_val_type != ASN_OCTET_STR) {
810             DEBUGMSGTL(("usmUser", "write to %s not ASN_OCTET_STR\n",
811                         fname));
812             return SNMP_ERR_WRONGTYPE;
813         }
814         if (var_val_len == 0) {
815             return SNMP_ERR_WRONGLENGTH;
816         }
817     } else if (action == RESERVE2) {
818         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
819             return SNMP_ERR_INCONSISTENTNAME;
820         } else {
821             if (snmp_oid_compare(uptr->authProtocol, uptr->authProtocolLen,
822                                  usmHMACMD5AuthProtocol,
823                                  sizeof(usmHMACMD5AuthProtocol) /
824                                  sizeof(oid)) == 0) {
825                 if (var_val_len != 0 && var_val_len != 32) {
826                     return SNMP_ERR_WRONGLENGTH;
827                 }
828             } else
829                 if (snmp_oid_compare
830                     (uptr->authProtocol, uptr->authProtocolLen,
831                      usmHMACSHA1AuthProtocol,
832                      sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid)) == 0) {
833                 if (var_val_len != 0 && var_val_len != 40) {
834                     return SNMP_ERR_WRONGLENGTH;
835                 }
836             }
837         }
838     } else if (action == ACTION) {
839         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
840             return SNMP_ERR_INCONSISTENTNAME;
841         }
842         if (uptr->cloneFrom == NULL) {
843             return SNMP_ERR_INCONSISTENTNAME;
844         }
845         if (snmp_oid_compare(uptr->authProtocol, uptr->authProtocolLen,
846                              usmNoAuthProtocol,
847                              sizeof(usmNoAuthProtocol) / sizeof(oid)) ==
848             0) {
849             /*
850              * "When the value of the corresponding usmUserAuthProtocol is
851              * usmNoAuthProtocol, then a set is successful, but effectively
852              * is a no-op."  
853              */
854             DEBUGMSGTL(("usmUser",
855                         "%s: noAuthProtocol keyChange... success!\n",
856                         fname));
857             return SNMP_ERR_NOERROR;
858         }
859
860         /*
861          * Change the key.  
862          */
863         DEBUGMSGTL(("usmUser", "%s: changing auth key for user %s\n",
864                     fname, uptr->secName));
865
866         if (decode_keychange(uptr->authProtocol, uptr->authProtocolLen,
867                              uptr->authKey, uptr->authKeyLen,
868                              var_val, var_val_len,
869                              buf, &buflen) != SNMPERR_SUCCESS) {
870             DEBUGMSGTL(("usmUser", "%s: ... failed\n", fname));
871             return SNMP_ERR_GENERR;
872         }
873         DEBUGMSGTL(("usmUser", "%s: ... succeeded\n", fname));
874         resetOnFail = 1;
875         oldkey = uptr->authKey;
876         oldkeylen = uptr->authKeyLen;
877         memdup(&uptr->authKey, buf, buflen);
878         if (uptr->authKey == NULL) {
879             return SNMP_ERR_RESOURCEUNAVAILABLE;
880         }
881         uptr->authKeyLen = buflen;
882     } else if (action == COMMIT) {
883         SNMP_FREE(oldkey);
884         oldkey = NULL;
885     } else if (action == UNDO) {
886         if ((uptr = usm_parse_user(name, name_len)) != NULL && resetOnFail) {
887             SNMP_FREE(uptr->authKey);
888             uptr->authKey = oldkey;
889             uptr->authKeyLen = oldkeylen;
890         }
891     }
892
893     return SNMP_ERR_NOERROR;
894 }                               /* end write_usmUserAuthKeyChange() */
895
896 int
897 write_usmUserPrivProtocol(int action,
898                           u_char * var_val,
899                           u_char var_val_type,
900                           size_t var_val_len,
901                           u_char * statP, oid * name, size_t name_len)
902 {
903     static oid     *optr;
904     static size_t   olen;
905     static int      resetOnFail;
906     struct usmUser *uptr;
907
908     if (action == RESERVE1) {
909         resetOnFail = 0;
910         if (var_val_type != ASN_OBJECT_ID) {
911             DEBUGMSGTL(("usmUser",
912                         "write to usmUserPrivProtocol not ASN_OBJECT_ID\n"));
913             return SNMP_ERR_WRONGTYPE;
914         }
915         if (var_val_len > USM_LENGTH_OID_MAX * sizeof(oid) ||
916             var_val_len % sizeof(oid) != 0) {
917             DEBUGMSGTL(("usmUser",
918                         "write to usmUserPrivProtocol: bad length\n"));
919             return SNMP_ERR_WRONGLENGTH;
920         }
921     } else if (action == RESERVE2) {
922         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
923             return SNMP_ERR_INCONSISTENTNAME;
924         }
925
926         if (uptr->userStatus == RS_ACTIVE
927             || uptr->userStatus == RS_NOTREADY
928             || uptr->userStatus == RS_NOTINSERVICE) {
929             /*
930              * The privProtocol is already set.  It is only legal to CHANGE it
931              * to usmNoPrivProtocol.  
932              */
933             if (snmp_oid_compare
934                 ((oid *) var_val, var_val_len / sizeof(oid),
935                  usmNoPrivProtocol,
936                  sizeof(usmNoPrivProtocol) / sizeof(oid)) == 0) {
937                 resetOnFail = 1;
938                 optr = uptr->privProtocol;
939                 olen = uptr->privProtocolLen;
940                 uptr->privProtocol = snmp_duplicate_objid((oid *) var_val,
941                                                           var_val_len /
942                                                           sizeof(oid));
943                 if (uptr->privProtocol == NULL) {
944                     return SNMP_ERR_RESOURCEUNAVAILABLE;
945                 }
946                 uptr->privProtocolLen = var_val_len / sizeof(oid);
947             } else
948                 if (snmp_oid_compare
949                     ((oid *) var_val, var_val_len / sizeof(oid),
950                      uptr->privProtocol, uptr->privProtocolLen) == 0) {
951                 /*
952                  * But it's also okay to set it to the same thing as it
953                  * currently is.  
954                  */
955                 return SNMP_ERR_NOERROR;
956             } else {
957                 return SNMP_ERR_INCONSISTENTVALUE;
958             }
959         } else {
960             /*
961              * This row is under creation.  It's okay to set
962              * usmUserPrivProtocol to any valid privProtocol with the proviso
963              * that if usmUserAuthProtocol is set to usmNoAuthProtocol, it may
964              * only be set to usmNoPrivProtocol.  The value will be overwritten
965              * when usmUserCloneFrom is set (so don't write it if that has
966              * already been set).  
967              */
968             if (snmp_oid_compare(uptr->authProtocol, uptr->authProtocolLen,
969                                  usmNoAuthProtocol,
970                                  sizeof(usmNoAuthProtocol) /
971                                  sizeof(oid)) == 0) {
972                 if (snmp_oid_compare
973                     ((oid *) var_val, var_val_len / sizeof(oid),
974                      usmNoPrivProtocol,
975                      sizeof(usmNoPrivProtocol) / sizeof(oid)) != 0) {
976                     return SNMP_ERR_INCONSISTENTVALUE;
977                 }
978             } else {
979                 if (snmp_oid_compare
980                     ((oid *) var_val, var_val_len / sizeof(oid),
981                      usmNoPrivProtocol,
982                      sizeof(usmNoPrivProtocol) / sizeof(oid)) != 0
983                     && snmp_oid_compare((oid *) var_val,
984                                         var_val_len / sizeof(oid),
985                                         usmDESPrivProtocol,
986                                         sizeof(usmDESPrivProtocol) /
987                                         sizeof(oid) != 0)) {
988                     return SNMP_ERR_WRONGVALUE;
989                 }
990             }
991             resetOnFail = 1;
992             optr = uptr->privProtocol;
993             olen = uptr->privProtocolLen;
994             uptr->privProtocol = snmp_duplicate_objid((oid *) var_val,
995                                                       var_val_len /
996                                                       sizeof(oid));
997             if (uptr->privProtocol == NULL) {
998                 return SNMP_ERR_RESOURCEUNAVAILABLE;
999             }
1000             uptr->privProtocolLen = var_val_len / sizeof(oid);
1001         }
1002     } else if (action == COMMIT) {
1003         SNMP_FREE(optr);
1004         optr = NULL;
1005     } else if (action == FREE || action == UNDO) {
1006         if ((uptr = usm_parse_user(name, name_len)) != NULL) {
1007             if (resetOnFail) {
1008                 SNMP_FREE(uptr->privProtocol);
1009                 uptr->privProtocol = optr;
1010                 uptr->privProtocolLen = olen;
1011             }
1012         }
1013     }
1014
1015     return SNMP_ERR_NOERROR;
1016 }                               /* end write_usmUserPrivProtocol() */
1017
1018 /*
1019  * Note: This function handles both the usmUserPrivKeyChange and
1020  *       usmUserOwnPrivKeyChange objects.  We are not passed the name
1021  *       of the user requseting the keychange, so we leave this to the
1022  *       calling module to verify when and if we should be called.  To
1023  *       change this would require a change in the mib module API to
1024  *       pass in the securityName requesting the change.
1025  *
1026  */
1027 int
1028 write_usmUserPrivKeyChange(int action,
1029                            u_char * var_val,
1030                            u_char var_val_type,
1031                            size_t var_val_len,
1032                            u_char * statP, oid * name, size_t name_len)
1033 {
1034     struct usmUser *uptr;
1035     unsigned char   buf[SNMP_MAXBUF_SMALL];
1036     size_t          buflen = SNMP_MAXBUF_SMALL;
1037     const char      fnPrivKey[] = "write_usmUserPrivKeyChange";
1038     const char      fnOwnPrivKey[] = "write_usmUserOwnPrivKeyChange";
1039     const char     *fname;
1040     static unsigned char *oldkey;
1041     static size_t   oldkeylen;
1042     static int      resetOnFail;
1043
1044     if (name[USM_MIB_LENGTH - 1] == 9) {
1045         fname = fnPrivKey;
1046     } else {
1047         fname = fnOwnPrivKey;
1048     }
1049
1050     if (action == RESERVE1) {
1051         resetOnFail = 0;
1052         if (var_val_type != ASN_OCTET_STR) {
1053             DEBUGMSGTL(("usmUser", "write to %s not ASN_OCTET_STR\n",
1054                         fname));
1055             return SNMP_ERR_WRONGTYPE;
1056         }
1057         if (var_val_len == 0) {
1058             return SNMP_ERR_WRONGLENGTH;
1059         }
1060     } else if (action == RESERVE2) {
1061         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
1062             return SNMP_ERR_INCONSISTENTNAME;
1063         } else {
1064             if (snmp_oid_compare(uptr->privProtocol, uptr->privProtocolLen,
1065                                  usmDESPrivProtocol,
1066                                  sizeof(usmDESPrivProtocol) /
1067                                  sizeof(oid)) == 0) {
1068                 if (var_val_len != 0 && var_val_len != 32) {
1069                     return SNMP_ERR_WRONGLENGTH;
1070                 }
1071             }
1072         }
1073     } else if (action == ACTION) {
1074         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
1075             return SNMP_ERR_INCONSISTENTNAME;
1076         }
1077         if (uptr->cloneFrom == NULL) {
1078             return SNMP_ERR_INCONSISTENTNAME;
1079         }
1080         if (snmp_oid_compare(uptr->privProtocol, uptr->privProtocolLen,
1081                              usmNoPrivProtocol,
1082                              sizeof(usmNoPrivProtocol) / sizeof(oid)) ==
1083             0) {
1084             /*
1085              * "When the value of the corresponding usmUserPrivProtocol is
1086              * usmNoPrivProtocol, then a set is successful, but effectively
1087              * is a no-op."  
1088              */
1089             DEBUGMSGTL(("usmUser",
1090                         "%s: noPrivProtocol keyChange... success!\n",
1091                         fname));
1092             return SNMP_ERR_NOERROR;
1093         }
1094
1095         /*
1096          * Change the key. 
1097          */
1098         DEBUGMSGTL(("usmUser", "%s: changing priv key for user %s\n",
1099                     fname, uptr->secName));
1100
1101         if (decode_keychange(uptr->authProtocol, uptr->authProtocolLen,
1102                              uptr->privKey, uptr->privKeyLen,
1103                              var_val, var_val_len,
1104                              buf, &buflen) != SNMPERR_SUCCESS) {
1105             DEBUGMSGTL(("usmUser", "%s: ... failed\n", fname));
1106             return SNMP_ERR_GENERR;
1107         }
1108         DEBUGMSGTL(("usmUser", "%s: ... succeeded\n", fname));
1109         resetOnFail = 1;
1110         oldkey = uptr->privKey;
1111         oldkeylen = uptr->privKeyLen;
1112         memdup(&uptr->privKey, buf, buflen);
1113         if (uptr->privKey == NULL) {
1114             return SNMP_ERR_RESOURCEUNAVAILABLE;
1115         }
1116         uptr->privKeyLen = buflen;
1117     } else if (action == COMMIT) {
1118         SNMP_FREE(oldkey);
1119         oldkey = NULL;
1120     } else if (action == UNDO) {
1121         if ((uptr = usm_parse_user(name, name_len)) != NULL && resetOnFail) {
1122             SNMP_FREE(uptr->privKey);
1123             uptr->privKey = oldkey;
1124             uptr->privKeyLen = oldkeylen;
1125         }
1126     }
1127
1128     return SNMP_ERR_NOERROR;
1129 }                               /* end write_usmUserPrivKeyChange() */
1130
1131 int
1132 write_usmUserPublic(int action,
1133                     u_char * var_val,
1134                     u_char var_val_type,
1135                     size_t var_val_len,
1136                     u_char * statP, oid * name, size_t name_len)
1137 {
1138     struct usmUser *uptr = NULL;
1139
1140     if (var_val_type != ASN_OCTET_STR) {
1141         DEBUGMSGTL(("usmUser",
1142                     "write to usmUserPublic not ASN_OCTET_STR\n"));
1143         return SNMP_ERR_WRONGTYPE;
1144     }
1145     if (var_val_len < 0 || var_val_len > 32) {
1146         DEBUGMSGTL(("usmUser", "write to usmUserPublic: bad length\n"));
1147         return SNMP_ERR_WRONGLENGTH;
1148     }
1149     if (action == COMMIT) {
1150         /*
1151          * don't allow creations here 
1152          */
1153         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
1154             return SNMP_ERR_NOSUCHNAME;
1155         }
1156         if (uptr->userPublicString)
1157             free(uptr->userPublicString);
1158         uptr->userPublicString = (u_char *) malloc(var_val_len + 1);
1159         if (uptr->userPublicString == NULL) {
1160             return SNMP_ERR_GENERR;
1161         }
1162         memcpy(uptr->userPublicString, var_val, var_val_len);
1163         uptr->userPublicString[var_val_len] = 0;
1164         DEBUGMSG(("usmUser", "setting public string: %d - %s\n",
1165                   var_val_len, uptr->userPublicString));
1166     }
1167     return SNMP_ERR_NOERROR;
1168 }                               /* end write_usmUserPublic() */
1169
1170 int
1171 write_usmUserStorageType(int action,
1172                          u_char * var_val,
1173                          u_char var_val_type,
1174                          size_t var_val_len,
1175                          u_char * statP, oid * name, size_t name_len)
1176 {
1177     long            long_ret = *((long *) var_val);
1178     static long     oldValue;
1179     struct usmUser *uptr;
1180     static int      resetOnFail;
1181
1182     if (action == RESERVE1) {
1183         resetOnFail = 0;
1184         if (var_val_type != ASN_INTEGER) {
1185             DEBUGMSGTL(("usmUser",
1186                         "write to usmUserStorageType not ASN_INTEGER\n"));
1187             return SNMP_ERR_WRONGTYPE;
1188         }
1189         if (var_val_len != sizeof(long)) {
1190             DEBUGMSGTL(("usmUser",
1191                         "write to usmUserStorageType: bad length\n"));
1192             return SNMP_ERR_WRONGLENGTH;
1193         }
1194         if (long_ret < 1 || long_ret > 5) {
1195             return SNMP_ERR_WRONGVALUE;
1196         }
1197     } else if (action == RESERVE2) {
1198         if ((uptr = usm_parse_user(name, name_len)) == NULL) {
1199             return SNMP_ERR_INCONSISTENTNAME;
1200         }
1201         if ((long_ret == ST_VOLATILE || long_ret == ST_NONVOLATILE) &&
1202             (uptr->userStorageType == ST_VOLATILE ||
1203              uptr->userStorageType == ST_NONVOLATILE)) {
1204             oldValue = uptr->userStorageType;
1205             uptr->userStorageType = long_ret;
1206             resetOnFail = 1;
1207         } else {
1208             /*
1209              * From RFC2574:
1210              * 
1211              * "Note that any user who employs authentication or privacy must
1212              * allow its secret(s) to be updated and thus cannot be 'readOnly'.
1213              * 
1214              * If an initial set operation tries to set the value to 'readOnly'
1215              * for a user who employs authentication or privacy, then an
1216              * 'inconsistentValue' error must be returned.  Note that if the
1217              * value has been previously set (implicit or explicit) to any
1218              * value, then the rules as defined in the StorageType Textual
1219              * Convention apply.  
1220              */
1221             DEBUGMSGTL(("usmUser",
1222                         "long_ret %d uptr->st %d uptr->status %d\n",
1223                         long_ret, uptr->userStorageType,
1224                         uptr->userStatus));
1225
1226             if (long_ret == ST_READONLY &&
1227                 uptr->userStorageType != ST_READONLY &&
1228                 (uptr->userStatus == RS_ACTIVE ||
1229                  uptr->userStatus == RS_NOTINSERVICE)) {
1230                 return SNMP_ERR_WRONGVALUE;
1231             } else if (long_ret == ST_READONLY &&
1232                        (snmp_oid_compare
1233                         (uptr->privProtocol, uptr->privProtocolLen,
1234                          usmNoPrivProtocol,
1235                          sizeof(usmNoPrivProtocol) / sizeof(oid)) != 0
1236                         || snmp_oid_compare(uptr->authProtocol,
1237                                             uptr->authProtocolLen,
1238                                             usmNoAuthProtocol,
1239                                             sizeof(usmNoAuthProtocol) /
1240                                             sizeof(oid)) != 0)) {
1241                 return SNMP_ERR_INCONSISTENTVALUE;
1242             } else {
1243                 return SNMP_ERR_WRONGVALUE;
1244             }
1245         }
1246     } else if (action == UNDO || action == FREE) {
1247         if ((uptr = usm_parse_user(name, name_len)) != NULL && resetOnFail) {
1248             uptr->userStorageType = oldValue;
1249         }
1250     }
1251     return SNMP_ERR_NOERROR;
1252 }                               /* end write_usmUserStorageType() */
1253
1254 /*
1255  * Return 1 if enough objects have been set up to transition rowStatus to
1256  * notInService(2) or active(1).  
1257  */
1258
1259 int
1260 usmStatusCheck(struct usmUser *uptr)
1261 {
1262     if (uptr == NULL) {
1263         return 0;
1264     } else {
1265         if (uptr->cloneFrom == NULL) {
1266             return 0;
1267         }
1268     }
1269     return 1;
1270 }
1271
1272 /*******************************************************************-o-******
1273  * write_usmUserStatus
1274  *
1275  * Parameters:
1276  *       action
1277  *      *var_val
1278  *       var_val_type
1279  *       var_val_len
1280  *      *statP
1281  *      *name
1282  *       name_len
1283  *      
1284  * Returns:
1285  *      SNMP_ERR_NOERROR                On success.
1286  *      SNMP_ERR_GENERR 
1287  *      SNMP_ERR_INCONSISTENTNAME
1288  *      SNMP_ERR_INCONSISTENTVALUE
1289  *      SNMP_ERR_WRONGLENGTH
1290  *      SNMP_ERR_WRONGTYPE
1291  */
1292 int
1293 write_usmUserStatus(int action,
1294                     u_char * var_val,
1295                     u_char var_val_type,
1296                     size_t var_val_len,
1297                     u_char * statP, oid * name, size_t name_len)
1298 {
1299     /*
1300      * variables we may use later 
1301      */
1302     static long     long_ret;
1303     unsigned char  *engineID;
1304     size_t          engineIDLen;
1305     char           *newName;
1306     size_t          nameLen;
1307     struct usmUser *uptr = NULL;
1308
1309     if (action == RESERVE1) {
1310         if (var_val_type != ASN_INTEGER) {
1311             DEBUGMSGTL(("usmUser",
1312                         "write to usmUserStatus not ASN_INTEGER\n"));
1313             return SNMP_ERR_WRONGTYPE;
1314         }
1315         if (var_val_len != sizeof(long_ret)) {
1316             DEBUGMSGTL(("usmUser",
1317                         "write to usmUserStatus: bad length\n"));
1318             return SNMP_ERR_WRONGLENGTH;
1319         }
1320         long_ret = *((long *) var_val);
1321         if (long_ret == RS_NOTREADY || long_ret < 1 || long_ret > 6) {
1322             return SNMP_ERR_WRONGVALUE;
1323         }
1324
1325         /*
1326          * See if we can parse the oid for engineID/name first.  
1327          */
1328         if (usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH,
1329                           &engineID, &engineIDLen, (u_char **) & newName,
1330                           &nameLen)) {
1331             return SNMP_ERR_INCONSISTENTNAME;
1332         }
1333
1334         if (engineIDLen < 5 || engineIDLen > 32 || nameLen < 1
1335             || nameLen > 32) {
1336             SNMP_FREE(engineID);
1337             SNMP_FREE(newName);
1338             return SNMP_ERR_NOCREATION;
1339         }
1340
1341         /*
1342          * Now see if a user already exists with these index values. 
1343          */
1344         uptr = usm_get_user(engineID, engineIDLen, newName);
1345
1346         if (uptr != NULL) {
1347             if (long_ret == RS_CREATEANDGO || long_ret == RS_CREATEANDWAIT) {
1348                 SNMP_FREE(engineID);
1349                 SNMP_FREE(newName);
1350                 long_ret = RS_NOTREADY;
1351                 return SNMP_ERR_INCONSISTENTVALUE;
1352             }
1353             SNMP_FREE(engineID);
1354             SNMP_FREE(newName);
1355         } else {
1356             if (long_ret == RS_ACTIVE || long_ret == RS_NOTINSERVICE) {
1357                 SNMP_FREE(engineID);
1358                 SNMP_FREE(newName);
1359                 return SNMP_ERR_INCONSISTENTVALUE;
1360             }
1361             if (long_ret == RS_CREATEANDGO || long_ret == RS_CREATEANDWAIT) {
1362                 if ((uptr = usm_create_user()) == NULL) {
1363                     SNMP_FREE(engineID);
1364                     SNMP_FREE(newName);
1365                     return SNMP_ERR_RESOURCEUNAVAILABLE;
1366                 }
1367                 uptr->engineID = engineID;
1368                 uptr->name = newName;
1369                 uptr->secName = strdup(uptr->name);
1370                 if (uptr->secName == NULL) {
1371                     usm_free_user(uptr);
1372                     return SNMP_ERR_RESOURCEUNAVAILABLE;
1373                 }
1374                 uptr->engineIDLen = engineIDLen;
1375
1376                 /*
1377                  * Set status to createAndGo or createAndWait so we can tell
1378                  * that this row is under creation.  
1379                  */
1380
1381                 uptr->userStatus = long_ret;
1382
1383                 /*
1384                  * Add to the list of users (we will take it off again
1385                  * later if something goes wrong).  
1386                  */
1387
1388                 usm_add_user(uptr);
1389             } else {
1390                 SNMP_FREE(engineID);
1391                 SNMP_FREE(newName);
1392             }
1393         }
1394     } else if (action == ACTION) {
1395         usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH,
1396                       &engineID, &engineIDLen, (u_char **) & newName,
1397                       &nameLen);
1398         uptr = usm_get_user(engineID, engineIDLen, newName);
1399         SNMP_FREE(engineID);
1400         SNMP_FREE(newName);
1401
1402         if (uptr != NULL) {
1403             if (long_ret == RS_CREATEANDGO || long_ret == RS_ACTIVE) {
1404                 if (usmStatusCheck(uptr)) {
1405                     uptr->userStatus = RS_ACTIVE;
1406                 } else {
1407                     SNMP_FREE(engineID);
1408                     SNMP_FREE(newName);
1409                     return SNMP_ERR_INCONSISTENTVALUE;
1410                 }
1411             } else if (long_ret == RS_CREATEANDWAIT) {
1412                 if (usmStatusCheck(uptr)) {
1413                     uptr->userStatus = RS_NOTINSERVICE;
1414                 } else {
1415                     uptr->userStatus = RS_NOTREADY;
1416                 }
1417             } else if (long_ret == RS_NOTINSERVICE) {
1418                 if (uptr->userStatus == RS_ACTIVE ||
1419                     uptr->userStatus == RS_NOTINSERVICE) {
1420                     uptr->userStatus = RS_NOTINSERVICE;
1421                 } else {
1422                     return SNMP_ERR_INCONSISTENTVALUE;
1423                 }
1424             }
1425         }
1426     } else if (action == COMMIT) {
1427         usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH,
1428                       &engineID, &engineIDLen, (u_char **) & newName,
1429                       &nameLen);
1430         uptr = usm_get_user(engineID, engineIDLen, newName);
1431         SNMP_FREE(engineID);
1432         SNMP_FREE(newName);
1433
1434         if (uptr != NULL) {
1435             if (long_ret == RS_DESTROY) {
1436                 usm_remove_user(uptr);
1437                 usm_free_user(uptr);
1438             }
1439         }
1440     } else if (action == UNDO || action == FREE) {
1441         usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH,
1442                       &engineID, &engineIDLen, (u_char **) & newName,
1443                       &nameLen);
1444         uptr = usm_get_user(engineID, engineIDLen, newName);
1445         SNMP_FREE(engineID);
1446         SNMP_FREE(newName);
1447
1448         if (long_ret == RS_CREATEANDGO || long_ret == RS_CREATEANDWAIT) {
1449             usm_remove_user(uptr);
1450             usm_free_user(uptr);
1451         }
1452     }
1453
1454     return SNMP_ERR_NOERROR;
1455 }
1456
1457 #if 0
1458
1459     /*
1460      * see if we can parse the oid for engineID/name first 
1461      */
1462 if (usm_parse_oid(&name[USM_MIB_LENGTH], name_len - USM_MIB_LENGTH,
1463                   &engineID, &engineIDLen, (u_char **) & newName,
1464                   &nameLen))
1465     return SNMP_ERR_INCONSISTENTNAME;
1466
1467     /*
1468      * Now see if a user already exists with these index values 
1469      */
1470 uptr = usm_get_user(engineID, engineIDLen, newName);
1471
1472
1473 if (uptr) {                     /* If so, we set the appropriate value... */
1474     free(engineID);
1475     free(newName);
1476     if (long_ret == RS_CREATEANDGO || long_ret == RS_CREATEANDWAIT) {
1477         return SNMP_ERR_INCONSISTENTVALUE;
1478     }
1479     if (long_ret == RS_DESTROY) {
1480         usm_remove_user(uptr);
1481         usm_free_user(uptr);
1482     } else {
1483         uptr->userStatus = long_ret;
1484     }
1485
1486 } else {                        /* ...else we create a new user */
1487     /*
1488      * check for a valid status column set 
1489      */
1490     if (long_ret == RS_ACTIVE || long_ret == RS_NOTINSERVICE) {
1491         free(engineID);
1492         free(newName);
1493         return SNMP_ERR_INCONSISTENTVALUE;
1494     }
1495     if (long_ret == RS_DESTROY) {
1496         /*
1497          * destroying a non-existent row is actually legal 
1498          */
1499         free(engineID);
1500         free(newName);
1501         return SNMP_ERR_NOERROR;
1502     }
1503
1504     /*
1505      * generate a new user 
1506      */
1507     if ((uptr = usm_create_user()) == NULL) {
1508         free(engineID);
1509         free(newName);
1510         return SNMP_ERR_GENERR;
1511     }
1512
1513     /*
1514      * copy in the engineID 
1515      */
1516     uptr->engineID = (unsigned char *) malloc(engineIDLen);
1517     if (uptr->engineID == NULL) {
1518         free(engineID);
1519         free(newName);
1520         usm_free_user(uptr);
1521         return SNMP_ERR_GENERR;
1522     }
1523     uptr->engineIDLen = engineIDLen;
1524     memcpy(uptr->engineID, engineID, engineIDLen);
1525     free(engineID);
1526
1527     /*
1528      * copy in the name and secname 
1529      */
1530     if ((uptr->name = strdup(newName)) == NULL) {
1531         free(newName);
1532         usm_free_user(uptr);
1533         return SNMP_ERR_GENERR;
1534     }
1535     free(newName);
1536     if ((uptr->secName = strdup(uptr->name)) == NULL) {
1537         usm_free_user(uptr);
1538         return SNMP_ERR_GENERR;
1539     }
1540
1541     /*
1542      * set the status of the row based on the request 
1543      */
1544     if (long_ret == RS_CREATEANDGO)
1545         uptr->userStatus = RS_ACTIVE;
1546     else if (long_ret == RS_CREATEANDWAIT)
1547         uptr->userStatus = RS_NOTINSERVICE;
1548
1549     /*
1550      * finally, add it to our list of users 
1551      */
1552     usm_add_user(uptr);
1553
1554 }                               /* endif -- uptr */
1555 }                               /* endif -- action==COMMIT */
1556
1557 return SNMP_ERR_NOERROR;
1558
1559 }                               /* end write_usmUserStatus() */
1560 #endif