/** * interface entry physaddr ioctl wrapper * * @param fd : socket fd to use w/ioctl, or -1 to open/close one * @param ifentry : ifentry to update * * @retval 0 : success * @retval -1 : invalid parameters * @retval -2 : couldn't create socket * @retval -3 : ioctl call failed * @retval -4 : malloc error */ int netsnmp_access_interface_ioctl_physaddr_get(int fd, netsnmp_interface_entry *ifentry) { struct ifreq ifrq; int rc = 0; DEBUGMSGTL(("access:interface:ioctl", "physaddr_get\n")); if((NULL != ifentry->paddr) && (ifentry->paddr_len != IFHWADDRLEN)) { SNMP_FREE(ifentry->paddr); } if(NULL == ifentry->paddr) ifentry->paddr = malloc(IFHWADDRLEN); if(NULL == ifentry->paddr) { rc = -4; } else { /* * NOTE: this ioctl does not guarantee 6 bytes of a physaddr. * In particular, a 'sit0' interface only appears to get back * 4 bytes of sa_data. Uncomment this memset, and suddenly * the sit interface will be 0:0:0:0:?:? where ? is whatever was * in the memory before. Not sure if this memset should be done * for every ioctl, as the rest seem to work ok... */ memset(ifrq.ifr_hwaddr.sa_data, (0), IFHWADDRLEN); ifentry->paddr_len = IFHWADDRLEN; rc = _ioctl_get(fd, SIOCGIFHWADDR, &ifrq, ifentry->name); if (rc < 0) { memset(ifentry->paddr, (0), IFHWADDRLEN); rc = -3; /* msg already logged */ } else { memcpy(ifentry->paddr, ifrq.ifr_hwaddr.sa_data, IFHWADDRLEN); /* * arphrd defines vary greatly. ETHER seems to be the only common one */ #ifdef ARPHRD_ETHER switch (ifrq.ifr_hwaddr.sa_family) { case ARPHRD_ETHER: ifentry->type = 6; break; #if defined(ARPHRD_TUNNEL) || defined(ARPHRD_IPGRE) || defined(ARPHRD_SIT) #ifdef ARPHRD_TUNNEL case ARPHRD_TUNNEL: case ARPHRD_TUNNEL6: #endif #ifdef ARPHRD_IPGRE case ARPHRD_IPGRE: #endif #ifdef ARPHRD_SIT case ARPHRD_SIT: #endif ifentry->type = 131; break; /* tunnel */ #endif #ifdef ARPHRD_SLIP case ARPHRD_SLIP: case ARPHRD_CSLIP: case ARPHRD_SLIP6: case ARPHRD_CSLIP6: ifentry->type = 28; break; /* slip */ #endif #ifdef ARPHRD_PPP case ARPHRD_PPP: ifentry->type = 23; break; /* ppp */ #endif #ifdef ARPHRD_LOOPBACK case ARPHRD_LOOPBACK: ifentry->type = 24; break; /* softwareLoopback */ #endif #ifdef ARPHRD_FDDI case ARPHRD_FDDI: ifentry->type = 15; break; #endif #ifdef ARPHRD_ARCNET case ARPHRD_ARCNET: ifentry->type = 35; break; #endif #ifdef ARPHRD_LOCALTLK case ARPHRD_LOCALTLK: ifentry->type = 42; break; #endif #ifdef ARPHRD_HIPPI case ARPHRD_HIPPI: ifentry->type = 47; break; #endif #ifdef ARPHRD_ATM case ARPHRD_ATM: ifentry->type = 37; break; #endif /* * XXX: more if_arp.h:ARPHRD_xxx to IANAifType mappings... */ default: DEBUGMSGTL(("access:interface:ioctl", "unknown entry type %d\n", ifrq.ifr_hwaddr.sa_family)); } /* switch */ #endif /* ARPHRD_LOOPBACK */ } } return rc; }
void sctpAssocRemAddrTable_entry_free(sctpAssocRemAddrTable_entry * entry) { if (entry != NULL) SNMP_FREE(entry); }
/** * copy an route_entry * * @retval -1 : error * @retval 0 : no error */ int netsnmp_access_route_entry_copy(netsnmp_route_entry *lhs, netsnmp_route_entry *rhs) { #if 0 /* no arch stuff in route (yet) */ int rc; /* * copy arch stuff. we don't care if it changed */ rc = netsnmp_arch_route_entry_copy(lhs,rhs); if (0 != rc) { snmp_log(LOG_ERR,"arch route copy failed\n"); return -1; } #endif lhs->if_index = rhs->if_index; lhs->rt_dest_len = rhs->rt_dest_len; memcpy(lhs->rt_dest, rhs->rt_dest, rhs->rt_dest_len); lhs->rt_dest_type = rhs->rt_dest_type; lhs->rt_nexthop_len = rhs->rt_nexthop_len; memcpy(lhs->rt_nexthop, rhs->rt_nexthop, rhs->rt_nexthop_len); lhs->rt_nexthop_type = rhs->rt_nexthop_type; #ifdef USING_IP_FORWARD_MIB_INETCIDRROUTETABLE_INETCIDRROUTETABLE_MODULE if (NULL != lhs->rt_policy) { if (NETSNMP_ACCESS_ROUTE_POLICY_STATIC & lhs->flags) lhs->rt_policy = NULL; else { SNMP_FREE(lhs->rt_policy); } } if (NULL != rhs->rt_policy) { if ((NETSNMP_ACCESS_ROUTE_POLICY_STATIC & rhs->flags) && ! (NETSNMP_ACCESS_ROUTE_POLICY_DEEP_COPY & rhs->flags)) { lhs->rt_policy = rhs->rt_policy; } else { snmp_clone_mem((void **) &lhs->rt_policy, rhs->rt_policy, rhs->rt_policy_len * sizeof(oid)); } } lhs->rt_policy_len = rhs->rt_policy_len; #endif lhs->rt_pfx_len = rhs->rt_pfx_len; lhs->rt_type = rhs->rt_type; lhs->rt_proto = rhs->rt_proto; #ifdef USING_IP_FORWARD_MIB_IPCIDRROUTETABLE_IPCIDRROUTETABLE_MODULE if (NULL != lhs->rt_info) SNMP_FREE(lhs->rt_info); if (NULL != rhs->rt_info) snmp_clone_mem((void **) &lhs->rt_info, rhs->rt_info, rhs->rt_info_len * sizeof(oid)); lhs->rt_info_len = rhs->rt_info_len; lhs->rt_mask = rhs->rt_mask; lhs->rt_tos = rhs->rt_tos; #endif lhs->rt_age = rhs->rt_age; lhs->rt_nexthop_as = rhs->rt_nexthop_as; lhs->rt_metric1 = rhs->rt_metric1; lhs->rt_metric2 = rhs->rt_metric2; lhs->rt_metric3 = rhs->rt_metric3; lhs->rt_metric4 = rhs->rt_metric4; lhs->rt_metric5 = rhs->rt_metric5; lhs->flags = rhs->flags; return 0; }
int main(int argc, char *argv[]) { netsnmp_session session, *ss; netsnmp_pdu *pdu = NULL, *response = NULL; int arg; size_t name_length = USM_OID_LEN; size_t name_length2 = USM_OID_LEN; int status; int exitval = 0; int rval; int command = 0; long longvar; size_t oldKu_len = SNMP_MAXBUF_SMALL, newKu_len = SNMP_MAXBUF_SMALL, oldkul_len = SNMP_MAXBUF_SMALL, oldkulpriv_len = SNMP_MAXBUF_SMALL, newkulpriv_len = SNMP_MAXBUF_SMALL, newkul_len = SNMP_MAXBUF_SMALL, keychange_len = SNMP_MAXBUF_SMALL, keychangepriv_len = SNMP_MAXBUF_SMALL; char *newpass = NULL, *oldpass = NULL; u_char oldKu[SNMP_MAXBUF_SMALL], newKu[SNMP_MAXBUF_SMALL], oldkul[SNMP_MAXBUF_SMALL], oldkulpriv[SNMP_MAXBUF_SMALL], newkulpriv[SNMP_MAXBUF_SMALL], newkul[SNMP_MAXBUF_SMALL], keychange[SNMP_MAXBUF_SMALL], keychangepriv[SNMP_MAXBUF_SMALL]; authKeyChange = authKeyOid; privKeyChange = privKeyOid; /* * get the common command line arguments */ switch (arg = snmp_parse_args(argc, argv, &session, "C:", optProc)) { case -2: exit(0); case -1: usage(); exit(1); default: break; } if (arg >= argc) { fprintf(stderr, "Please specify an operation to perform.\n"); usage(); exit(1); } SOCK_STARTUP; /* * open an SNMP session */ /* * Note: this needs to obtain the engineID used below */ session.flags &= ~SNMP_FLAGS_DONT_PROBE; ss = snmp_open(&session); if (ss == NULL) { /* * diagnose snmp_open errors with the input netsnmp_session pointer */ snmp_sess_perror("snmpusm", &session); exit(1); } /* * set usmUserEngineID from ss->contextEngineID * if not already set (via -CE) */ if (usmUserEngineID == NULL) { usmUserEngineID = ss->contextEngineID; usmUserEngineIDLen = ss->contextEngineIDLen; } /* * create PDU for SET request and add object names and values to request */ pdu = snmp_pdu_create(SNMP_MSG_SET); if (!pdu) { fprintf(stderr, "Failed to create request\n"); exit(1); } if (strcmp(argv[arg], CMD_PASSWD_NAME) == 0) { /* * passwd: change a users password. * * XXX: Uses the auth type of the calling user, a MD5 user can't * change a SHA user's key. */ char *passwd_user; command = CMD_PASSWD; oldpass = argv[++arg]; newpass = argv[++arg]; passwd_user = argv[++arg]; if (doprivkey == 0 && doauthkey == 0) doprivkey = doauthkey = 1; if (newpass == NULL || strlen(newpass) < USM_LENGTH_P_MIN) { fprintf(stderr, "New passphrase must be greater than %d characters in length.\n", USM_LENGTH_P_MIN); exit(1); } if (oldpass == NULL || strlen(oldpass) < USM_LENGTH_P_MIN) { fprintf(stderr, "Old passphrase must be greater than %d characters in length.\n", USM_LENGTH_P_MIN); exit(1); } /* * Change the user supplied on command line. */ if ((passwd_user != NULL) && (strlen(passwd_user) > 0)) { session.securityName = passwd_user; } else { /* * Use own key object if no user was supplied. */ authKeyChange = ownAuthKeyOid; privKeyChange = ownPrivKeyOid; } /* * do we have a securityName? If not, copy the default */ if (session.securityName == NULL) { session.securityName = strdup(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECNAME)); } /* * the old Ku is in the session, but we need the new one */ if (session.securityAuthProto == NULL) { /* * get .conf set default */ const oid *def = get_default_authtype(&session.securityAuthProtoLen); session.securityAuthProto = snmp_duplicate_objid(def, session.securityAuthProtoLen); } if (session.securityAuthProto == NULL) { /* * assume MD5 */ #ifndef NETSNMP_DISABLE_MD5 session.securityAuthProtoLen = sizeof(usmHMACMD5AuthProtocol) / sizeof(oid); session.securityAuthProto = snmp_duplicate_objid(usmHMACMD5AuthProtocol, session.securityAuthProtoLen); #else session.securityAuthProtoLen = sizeof(usmHMACSHA1AuthProtocol) / sizeof(oid); session.securityAuthProto = snmp_duplicate_objid(usmHMACSHA1AuthProtocol, session.securityAuthProtoLen); #endif } if (uselocalizedkey && (strncmp(oldpass, "0x", 2) == 0)) { /* * use the localized key from the command line */ u_char *buf; size_t buf_len = SNMP_MAXBUF_SMALL; buf = (u_char *) malloc (buf_len * sizeof(u_char)); oldkul_len = 0; /* initialize the offset */ if (!snmp_hex_to_binary((u_char **) (&buf), &buf_len, &oldkul_len, 0, oldpass)) { snmp_perror(argv[0]); fprintf(stderr, "generating the old Kul from localized key failed\n"); exit(1); } memcpy(oldkul, buf, oldkul_len); SNMP_FREE(buf); } else { /* * the old Ku is in the session, but we need the new one */ rval = generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *) oldpass, strlen(oldpass), oldKu, &oldKu_len); if (rval != SNMPERR_SUCCESS) { snmp_perror(argv[0]); fprintf(stderr, "generating the old Ku failed\n"); exit(1); } /* * generate the two Kul's */ rval = generate_kul(session.securityAuthProto, session.securityAuthProtoLen, usmUserEngineID, usmUserEngineIDLen, oldKu, oldKu_len, oldkul, &oldkul_len); if (rval != SNMPERR_SUCCESS) { snmp_perror(argv[0]); fprintf(stderr, "generating the old Kul failed\n"); exit(1); } } if (uselocalizedkey && (strncmp(newpass, "0x", 2) == 0)) { /* * use the localized key from the command line */ u_char *buf; size_t buf_len = SNMP_MAXBUF_SMALL; buf = (u_char *) malloc (buf_len * sizeof(u_char)); newkul_len = 0; /* initialize the offset */ if (!snmp_hex_to_binary((u_char **) (&buf), &buf_len, &newkul_len, 0, newpass)) { snmp_perror(argv[0]); fprintf(stderr, "generating the new Kul from localized key failed\n"); exit(1); } memcpy(newkul, buf, newkul_len); SNMP_FREE(buf); } else { rval = generate_Ku(session.securityAuthProto, session.securityAuthProtoLen, (u_char *) newpass, strlen(newpass), newKu, &newKu_len); if (rval != SNMPERR_SUCCESS) { snmp_perror(argv[0]); fprintf(stderr, "generating the new Ku failed\n"); exit(1); } rval = generate_kul(session.securityAuthProto, session.securityAuthProtoLen, usmUserEngineID, usmUserEngineIDLen, newKu, newKu_len, newkul, &newkul_len); if (rval != SNMPERR_SUCCESS) { snmp_perror(argv[0]); fprintf(stderr, "generating the new Kul failed\n"); exit(1); } } /* * for encryption, we may need to truncate the key to the proper length * so we need two copies. For simplicity, we always just copy even if * they're the same lengths. */ if (doprivkey) { if (!session.securityPrivProto) { snmp_log(LOG_ERR, "no encryption type specified, which I need in order to know to change the key\n"); exit(1); } #ifndef NETSNMP_DISABLE_DES if (ISTRANSFORM(session.securityPrivProto, DESPriv)) { /* DES uses a 128 bit key, 64 bits of which is a salt */ oldkulpriv_len = newkulpriv_len = 16; } #endif #ifdef HAVE_AES if (ISTRANSFORM(session.securityPrivProto, AESPriv)) { oldkulpriv_len = newkulpriv_len = 16; } #endif memcpy(oldkulpriv, oldkul, oldkulpriv_len); memcpy(newkulpriv, newkul, newkulpriv_len); } /* * create the keychange string */ if (doauthkey) { rval = encode_keychange(session.securityAuthProto, session.securityAuthProtoLen, oldkul, oldkul_len, newkul, newkul_len, keychange, &keychange_len); if (rval != SNMPERR_SUCCESS) { snmp_perror(argv[0]); fprintf(stderr, "encoding the keychange failed\n"); usage(); exit(1); } } /* which is slightly different for encryption if lengths are different */ if (doprivkey) { rval = encode_keychange(session.securityAuthProto, session.securityAuthProtoLen, oldkulpriv, oldkulpriv_len, newkulpriv, newkulpriv_len, keychangepriv, &keychangepriv_len); if (rval != SNMPERR_SUCCESS) { snmp_perror(argv[0]); fprintf(stderr, "encoding the keychange failed\n"); usage(); exit(1); } } /* * add the keychange string to the outgoing packet */ if (doauthkey) { setup_oid(authKeyChange, &name_length, usmUserEngineID, usmUserEngineIDLen, session.securityName); snmp_pdu_add_variable(pdu, authKeyChange, name_length, ASN_OCTET_STR, keychange, keychange_len); } if (doprivkey) { setup_oid(privKeyChange, &name_length2, usmUserEngineID, usmUserEngineIDLen, session.securityName); snmp_pdu_add_variable(pdu, privKeyChange, name_length2, ASN_OCTET_STR, keychangepriv, keychangepriv_len); } } else if (strcmp(argv[arg], CMD_CREATE_NAME) == 0) { /* * create: create a user * * create USER [CLONEFROM] */ if (++arg >= argc) { fprintf(stderr, "You must specify the user name to create\n"); usage(); exit(1); } command = CMD_CREATE; if (++arg < argc) { /* * clone the new user from an existing user * (and make them active immediately) */ setup_oid(usmUserStatus, &name_length, usmUserEngineID, usmUserEngineIDLen, argv[arg-1]); longvar = RS_CREATEANDGO; snmp_pdu_add_variable(pdu, usmUserStatus, name_length, ASN_INTEGER, (u_char *) & longvar, sizeof(longvar)); name_length = USM_OID_LEN; setup_oid(usmUserCloneFrom, &name_length, usmUserEngineID, usmUserEngineIDLen, argv[arg - 1]); setup_oid(usmUserSecurityName, &name_length2, usmUserEngineID, usmUserEngineIDLen, argv[arg]); snmp_pdu_add_variable(pdu, usmUserCloneFrom, name_length, ASN_OBJECT_ID, (u_char *) usmUserSecurityName, sizeof(oid) * name_length2); } else { /* * create a new (unauthenticated) user from scratch * The Net-SNMP agent won't allow such a user to be made active. */ setup_oid(usmUserStatus, &name_length, usmUserEngineID, usmUserEngineIDLen, argv[arg-1]); longvar = RS_CREATEANDWAIT; snmp_pdu_add_variable(pdu, usmUserStatus, name_length, ASN_INTEGER, (u_char *) & longvar, sizeof(longvar)); } } else if (strcmp(argv[arg], CMD_CLONEFROM_NAME) == 0) { /* * create: clone a user from another * * cloneFrom USER FROM */ if (++arg >= argc) { fprintf(stderr, "You must specify the user name to operate on\n"); usage(); exit(1); } command = CMD_CLONEFROM; setup_oid(usmUserStatus, &name_length, usmUserEngineID, usmUserEngineIDLen, argv[arg]); longvar = RS_ACTIVE; snmp_pdu_add_variable(pdu, usmUserStatus, name_length, ASN_INTEGER, (u_char *) & longvar, sizeof(longvar)); name_length = USM_OID_LEN; setup_oid(usmUserCloneFrom, &name_length, usmUserEngineID, usmUserEngineIDLen, argv[arg]); if (++arg >= argc) { fprintf(stderr, "You must specify the user name to clone from\n"); usage(); exit(1); } setup_oid(usmUserSecurityName, &name_length2, usmUserEngineID, usmUserEngineIDLen, argv[arg]); snmp_pdu_add_variable(pdu, usmUserCloneFrom, name_length, ASN_OBJECT_ID, (u_char *) usmUserSecurityName, sizeof(oid) * name_length2); } else if (strcmp(argv[arg], CMD_DELETE_NAME) == 0) { /* * delete: delete a user * * delete USER */ if (++arg >= argc) { fprintf(stderr, "You must specify the user name to delete\n"); exit(1); } command = CMD_DELETE; setup_oid(usmUserStatus, &name_length, usmUserEngineID, usmUserEngineIDLen, argv[arg]); longvar = RS_DESTROY; snmp_pdu_add_variable(pdu, usmUserStatus, name_length, ASN_INTEGER, (u_char *) & longvar, sizeof(longvar)); } else if (strcmp(argv[arg], CMD_ACTIVATE_NAME) == 0) { /* * activate: activate a user * * activate USER */ if (++arg >= argc) { fprintf(stderr, "You must specify the user name to activate\n"); exit(1); } command = CMD_ACTIVATE; setup_oid(usmUserStatus, &name_length, usmUserEngineID, usmUserEngineIDLen, argv[arg]); longvar = RS_ACTIVE; snmp_pdu_add_variable(pdu, usmUserStatus, name_length, ASN_INTEGER, (u_char *) & longvar, sizeof(longvar)); } else if (strcmp(argv[arg], CMD_DEACTIVATE_NAME) == 0) { /* * deactivate: deactivate a user * * deactivate USER */ if (++arg >= argc) { fprintf(stderr, "You must specify the user name to deactivate\n"); exit(1); } command = CMD_DEACTIVATE; setup_oid(usmUserStatus, &name_length, usmUserEngineID, usmUserEngineIDLen, argv[arg]); longvar = RS_NOTINSERVICE; snmp_pdu_add_variable(pdu, usmUserStatus, name_length, ASN_INTEGER, (u_char *) & longvar, sizeof(longvar)); #if defined(HAVE_OPENSSL_DH_H) && defined(HAVE_LIBCRYPTO) } else if (strcmp(argv[arg], CMD_CHANGEKEY_NAME) == 0) { /* * change the key of a user if DH is available */ char *passwd_user; netsnmp_pdu *dhpdu, *dhresponse = NULL; netsnmp_variable_list *vars, *dhvar; command = CMD_CHANGEKEY; name_length = DH_USM_OID_LEN; name_length2 = DH_USM_OID_LEN; passwd_user = argv[++arg]; if (doprivkey == 0 && doauthkey == 0) doprivkey = doauthkey = 1; /* * Change the user supplied on command line. */ if ((passwd_user != NULL) && (strlen(passwd_user) > 0)) { session.securityName = passwd_user; } else { /* * Use own key object if no user was supplied. */ dhauthKeyChange = usmDHUserOwnAuthKeyChange; dhprivKeyChange = usmDHUserOwnPrivKeyChange; } /* * do we have a securityName? If not, copy the default */ if (session.securityName == NULL) { session.securityName = strdup(netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_SECNAME)); } /* fetch the needed diffie helman parameters */ dhpdu = snmp_pdu_create(SNMP_MSG_GET); if (!dhpdu) { fprintf(stderr, "Failed to create DH request\n"); exit(1); } /* get the current DH parameters */ snmp_add_null_var(dhpdu, usmDHParameters, usmDHParameters_len); /* maybe the auth key public value */ if (doauthkey) { setup_oid(dhauthKeyChange, &name_length, usmUserEngineID, usmUserEngineIDLen, session.securityName); snmp_add_null_var(dhpdu, dhauthKeyChange, name_length); } /* maybe the priv key public value */ if (doprivkey) { setup_oid(dhprivKeyChange, &name_length2, usmUserEngineID, usmUserEngineIDLen, session.securityName); snmp_add_null_var(dhpdu, dhprivKeyChange, name_length2); } /* fetch the values */ status = snmp_synch_response(ss, dhpdu, &dhresponse); if (status != SNMPERR_SUCCESS || dhresponse == NULL || dhresponse->errstat != SNMP_ERR_NOERROR || dhresponse->variables->type != ASN_OCTET_STR) { snmp_sess_perror("snmpusm", ss); if (dhresponse && dhresponse->variables && dhresponse->variables->type != ASN_OCTET_STR) { fprintf(stderr, "Can't get diffie-helman exchange from the agent\n"); fprintf(stderr, " (maybe it doesn't support the SNMP-USM-DH-OBJECTS-MIB MIB)\n"); } exitval = 1; goto begone; } dhvar = dhresponse->variables; vars = dhvar->next_variable; /* complete the DH equation & print resulting keys */ if (doauthkey) { if (get_USM_DH_key(vars, dhvar, sc_get_properlength(ss->securityAuthProto, ss->securityAuthProtoLen), pdu, "auth", dhauthKeyChange, name_length) != SNMPERR_SUCCESS) goto begone; vars = vars->next_variable; } if (doprivkey) { size_t dhprivKeyLen = 0; #ifndef NETSNMP_DISABLE_DES if (ISTRANSFORM(ss->securityPrivProto, DESPriv)) { /* DES uses a 128 bit key, 64 bits of which is a salt */ dhprivKeyLen = 16; } #endif #ifdef HAVE_AES if (ISTRANSFORM(ss->securityPrivProto, AESPriv)) { dhprivKeyLen = 16; } #endif if (get_USM_DH_key(vars, dhvar, dhprivKeyLen, pdu, "priv", dhprivKeyChange, name_length2) != SNMPERR_SUCCESS) goto begone; vars = vars->next_variable; } /* snmp_free_pdu(dhresponse); */ /* parts still in use somewhere */ #endif /* HAVE_OPENSSL_DH_H */ } else { fprintf(stderr, "Unknown command\n"); usage(); exit(1); } /* * add usmUserPublic if specified (via -Cp) */ if (usmUserPublic_val) { name_length = USM_OID_LEN; setup_oid(usmUserPublic, &name_length, usmUserEngineID, usmUserEngineIDLen, session.securityName); snmp_pdu_add_variable(pdu, usmUserPublic, name_length, ASN_OCTET_STR, usmUserPublic_val, strlen(usmUserPublic_val)); } /* * do the request */ status = snmp_synch_response(ss, pdu, &response); if (status == STAT_SUCCESS) { if (response) { if (response->errstat == SNMP_ERR_NOERROR) { fprintf(stdout, "%s\n", successNotes[command - 1]); } else { fprintf(stderr, "Error in packet.\nReason: %s\n", snmp_errstring(response->errstat)); if (response->errindex != 0) { int count; netsnmp_variable_list *vars; fprintf(stderr, "Failed object: "); for (count = 1, vars = response->variables; vars && count != response->errindex; vars = vars->next_variable, count++) /*EMPTY*/; if (vars) fprint_objid(stderr, vars->name, vars->name_length); fprintf(stderr, "\n"); } exitval = 2; } } } else if (status == STAT_TIMEOUT) { fprintf(stderr, "Timeout: No Response from %s\n", session.peername); exitval = 1; } else { /* status == STAT_ERROR */ snmp_sess_perror("snmpset", ss); exitval = 1; } begone: if (response) snmp_free_pdu(response); snmp_close(ss); SOCK_CLEANUP; return exitval; }
/* * Print a description of the network interfaces. */ void intpr(int interval) { oid ifcol_oid[] = { 1,3,6,1,2,1,2,2,1,0 }; size_t ifcol_len = OID_LENGTH( ifcol_oid ); struct _if_info *if_head, *if_tail, *cur_if; netsnmp_variable_list *var, *vp; /* * Track maximum field widths, expanding as necessary * This is one reason why results can't be * displayed immediately they are retrieved. */ int max_name = 4, max_ip = 7, max_route = 7, max_outq = 5; int max_ipkts = 5, max_ierrs = 5, max_opkts = 5, max_oerrs = 5; int max_ibytes = 6, max_obytes = 6; int i; if (interval) { sidewaysintpr((unsigned)interval); return; } /* * The traditional "netstat -i" output combines information * from two SNMP tables: * ipAddrTable (for the IP address/network) * ifTable (for the interface statistics) * * The previous approach was to retrieve (and save) the * address information first. Then walk the main ifTable, * add the relevant stored addresses, and saving the * full information for each interface, before displaying * the results as a separate pass. * * This code reverses this general structure, by first retrieving * (and storing) the interface statistics for the whole table, * then inserting the address information obtained from the * ipAddrTable, and finally displaying the results. * Such an arrangement should make it easier to extend this * to handle non-IP interfaces (hence not in ipAddrTable) */ if_head = NULL; if_tail = NULL; var = NULL; #define ADD_IFVAR( x ) ifcol_oid[ ifcol_len-1 ] = x; \ snmp_varlist_add_variable( &var, ifcol_oid, ifcol_len, ASN_NULL, NULL, 0) ADD_IFVAR( 2 ); /* ifName */ ADD_IFVAR( 4 ); /* ifMtu */ ADD_IFVAR( 8 ); /* ifOperStatus */ /* * The Net/Open-BSD behaviour is to display *either* byte * counts *or* packet/error counts (but not both). FreeBSD * integrates the byte counts into the traditional display. * * The previous 'snmpnetstat' implementation followed the * separatist model. This re-write offers an opportunity * to adopt the (more useful, IMO) Free-BSD approach. * * Or we could perhaps support both styles? :-) */ if (bflag || oflag) { ADD_IFVAR( 10 ); /* ifInOctets */ ADD_IFVAR( 16 ); /* ifOutOctets */ } if (!oflag) { ADD_IFVAR( 11 ); /* ifInUcastPkts */ ADD_IFVAR( 12 ); /* ifInNUcastPkts */ ADD_IFVAR( 14 ); /* ifInErrors */ ADD_IFVAR( 17 ); /* ifOutUcastPkts */ ADD_IFVAR( 18 ); /* ifOutNUcastPkts */ ADD_IFVAR( 20 ); /* ifOutErrors */ ADD_IFVAR( 21 ); /* ifOutQLen */ } #if 0 if (tflag) { ADD_IFVAR( XX ); /* ??? */ } #endif if (dflag) { ADD_IFVAR( 19 ); /* ifOutDiscards */ } #undef ADD_IFVAR /* * Now walk the ifTable, creating a list of interfaces */ while ( 1 ) { if (netsnmp_query_getnext( var, ss ) != SNMP_ERR_NOERROR) break; ifcol_oid[ ifcol_len-1 ] = 2; /* ifDescr */ if ( snmp_oid_compare( ifcol_oid, ifcol_len, var->name, ifcol_len) != 0 ) break; /* End of Table */ cur_if = SNMP_MALLOC_TYPEDEF( struct _if_info ); if (!cur_if) break; cur_if->ifindex = var->name[ var->name_length-1 ]; for ( vp=var; vp; vp=vp->next_variable ) { if ( ! vp->val.integer ) continue; if ( var->name[ var->name_length-1 ] != cur_if->ifindex ) { /* * Inconsistent index information * XXX - Try to recover ? */ SNMP_FREE( cur_if ); cur_if = NULL; break; /* not for now, no */ } switch ( vp->name[ var->name_length-2 ] ) { case 2: /* ifDescr */ if (vp->val_len >= sizeof(cur_if->name)) vp->val_len = sizeof(cur_if->name)-1; memmove( cur_if->name, vp->val.string, vp->val_len ); cur_if->name[vp->val_len] = 0; if ((i = strlen(cur_if->name) + 1) > max_name) max_name = i; break; case 4: /* ifMtu */ cur_if->mtu = *vp->val.integer; break; case 8: /* ifOperStatus */ cur_if->operstatus = *vp->val.integer; /* XXX - any special processing ?? */ break; case 10: /* ifInOctets */ sprintf(cur_if->s_ibytes, "%lu", *vp->val.integer); i = strlen(cur_if->s_ibytes); if (i > max_ibytes) max_ibytes = i; break; case 11: /* ifInUcastPkts */ cur_if->ipkts += *vp->val.integer; sprintf(cur_if->s_ipkts, "%lu", cur_if->ipkts); i = strlen(cur_if->s_ipkts); if (i > max_ipkts) max_ipkts = i; break; case 12: /* ifInNUcastPkts */ cur_if->ipkts += *vp->val.integer; sprintf(cur_if->s_ipkts, "%lu", cur_if->ipkts); i = strlen(cur_if->s_ipkts); if (i > max_ipkts) max_ipkts = i; break; case 14: /* ifInErrors */ sprintf(cur_if->s_ierrs, "%lu", *vp->val.integer); i = strlen(cur_if->s_ierrs); if (i > max_ierrs) max_ierrs = i; break; case 16: /* ifOutOctets */ sprintf(cur_if->s_obytes, "%lu", *vp->val.integer); i = strlen(cur_if->s_obytes); if (i > max_obytes) max_obytes = i; break; case 17: /* ifOutUcastPkts */ cur_if->opkts += *vp->val.integer; sprintf(cur_if->s_opkts, "%lu", cur_if->opkts); i = strlen(cur_if->s_opkts); if (i > max_opkts) max_opkts = i; break; case 18: /* ifOutNUcastPkts */ cur_if->opkts += *vp->val.integer; sprintf(cur_if->s_opkts, "%lu", cur_if->opkts); i = strlen(cur_if->s_opkts); if (i > max_opkts) max_opkts = i; break; case 19: /* ifOutDiscards */ cur_if->drops = *vp->val.integer; break; case 20: /* ifOutErrors */ sprintf(cur_if->s_oerrs, "%lu", *vp->val.integer); i = strlen(cur_if->s_oerrs); if (i > max_oerrs) max_oerrs = i; break; case 21: /* ifOutQLen */ sprintf(cur_if->s_outq, "%lu", *vp->val.integer); i = strlen(cur_if->s_outq); if (i > max_outq) max_outq = i; break; } } /* * XXX - Perhaps query ifXTable for additional info ?? * (ifName/ifAlias, or HC counters) */ /* * If we're to monitor a particular interface, then * ignore all others. It would be more efficient * to check this earlier (as part of processing * the varbind list). But performing this test here * means we can recognise ifXTable names as well) */ if ( intrface && strcmp( cur_if->name, intrface ) != 0) { SNMP_FREE( cur_if ); cur_if = NULL; } /* * Insert the IP address and network settings, and * add the new _if_stat structure to the list. */ if ( cur_if ) { _set_address( cur_if ); i = strlen(cur_if->ip); if (i > max_ip) max_ip = i; i = strlen(cur_if->route); if (i > max_route) max_route = i; if ( if_tail ) { if_tail->next = cur_if; if_tail = cur_if; } else { if_head = cur_if; if_tail = cur_if; } } } /* while (1) */ /* * Now display the specified results (in Free-BSD format) * setting the field widths appropriately.... */ printf("%*.*s %5.5s %*.*s %*.*s", -max_name, max_name, "Name", "Mtu", -max_route, max_route, "Network", -max_ip, max_ip, "Address"); if (oflag) { printf(" %*s %*s", max_ibytes, "Ibytes", max_obytes, "Obytes"); } else { printf(" %*s %*s", max_ipkts, "Ipkts", max_ierrs, "Ierrs"); if (bflag) printf(" %*s", max_ibytes, "Ibytes"); printf(" %*s %*s", max_opkts, "Opkts", max_oerrs, "Oerrs"); if (bflag) printf(" %*s", max_obytes, "Obytes"); printf(" %*s", max_outq, "Queue"); } /* if (tflag) printf(" %s", "Time"); */ if (dflag) printf(" %s", "Drop"); putchar('\n'); for (cur_if = if_head; cur_if; cur_if=cur_if->next) { if (cur_if->name[0] == 0) continue; printf( "%*.*s %5d", -max_name, max_name, cur_if->name, cur_if->mtu); printf(" %*.*s", -max_route, max_route, cur_if->route); printf(" %*.*s", -max_ip, max_ip, cur_if->ip); if (oflag) { printf(" %*s %*s", max_ibytes, cur_if->s_ibytes, max_obytes, cur_if->s_obytes); } else { printf(" %*s %*s", max_ipkts, cur_if->s_ipkts, max_ierrs, cur_if->s_ierrs); if (bflag) printf(" %*s", max_ibytes, cur_if->s_ibytes); printf(" %*s %*s", max_opkts, cur_if->s_opkts, max_oerrs, cur_if->s_oerrs); if (bflag) printf(" %*s", max_obytes, cur_if->s_obytes); printf(" %*s", max_outq, cur_if->s_outq); } /* if (tflag) printf(" %4d", cur_if->???); */ if (dflag) printf(" %4d", cur_if->drops); putchar('\n'); } /* * ... and tidy up. */ for (cur_if = if_head; cur_if; cur_if=if_head) { if_head=cur_if->next; cur_if->next = NULL; SNMP_FREE( cur_if ); } }
int vacm_check_view_contents(netsnmp_pdu *pdu, oid * name, size_t namelen, int check_subtree, int viewtype, int flags) { struct vacm_accessEntry *ap; struct vacm_groupEntry *gp; struct vacm_viewEntry *vp; char vacm_default_context[1] = ""; const char *contextName = vacm_default_context; const char *sn = NULL; char *vn; const char *pdu_community; /* * len defined by the vacmContextName object */ #define CONTEXTNAMEINDEXLEN 32 char contextNameIndex[CONTEXTNAMEINDEXLEN + 1]; #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) #if defined(NETSNMP_DISABLE_SNMPV1) if (pdu->version == SNMP_VERSION_2c) #else #if defined(NETSNMP_DISABLE_SNMPV2C) if (pdu->version == SNMP_VERSION_1) #else if (pdu->version == SNMP_VERSION_1 || pdu->version == SNMP_VERSION_2c) #endif #endif { pdu_community = (const char *) pdu->community; if (!pdu_community) pdu_community = ""; if (snmp_get_do_debugging()) { char *buf; if (pdu->community) { buf = (char *) malloc(1 + pdu->community_len); memcpy(buf, pdu->community, pdu->community_len); buf[pdu->community_len] = '\0'; } else { DEBUGMSGTL(("mibII/vacm_vars", "NULL community")); buf = strdup("NULL"); } DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: ver=%ld, community=%s\n", pdu->version, buf)); free(buf); } /* * Okay, if this PDU was received from a UDP or a TCP transport then * ask the transport abstraction layer to map its source address and * community string to a security name for us. */ if (0) { #ifdef NETSNMP_TRANSPORT_UDP_DOMAIN } else if (pdu->tDomain == netsnmpUDPDomain #ifdef NETSNMP_TRANSPORT_TCP_DOMAIN || pdu->tDomain == netsnmp_snmpTCPDomain #endif ) { if (!netsnmp_udp_getSecName(pdu->transport_data, pdu->transport_data_length, pdu_community, pdu->community_len, &sn, &contextName)) { /* * There are no com2sec entries. */ sn = NULL; } /* force the community -> context name mapping here */ SNMP_FREE(pdu->contextName); pdu->contextName = strdup(contextName); pdu->contextNameLen = strlen(contextName); #endif #ifdef NETSNMP_TRANSPORT_UDPIPV6_DOMAIN } else if (pdu->tDomain == netsnmp_UDPIPv6Domain #ifdef NETSNMP_TRANSPORT_TCPIPV6_DOMAIN || pdu->tDomain == netsnmp_TCPIPv6Domain #endif ) { if (!netsnmp_udp6_getSecName(pdu->transport_data, pdu->transport_data_length, pdu_community, pdu->community_len, &sn, &contextName)) { /* * There are no com2sec entries. */ sn = NULL; } /* force the community -> context name mapping here */ SNMP_FREE(pdu->contextName); pdu->contextName = strdup(contextName); pdu->contextNameLen = strlen(contextName); #endif #ifdef NETSNMP_TRANSPORT_UNIX_DOMAIN } else if (pdu->tDomain == netsnmp_UnixDomain){ if (!netsnmp_unix_getSecName(pdu->transport_data, pdu->transport_data_length, pdu_community, pdu->community_len, &sn, &contextName)) { sn = NULL; } /* force the community -> context name mapping here */ SNMP_FREE(pdu->contextName); pdu->contextName = strdup(contextName); pdu->contextNameLen = strlen(contextName); #endif } else { /* * Map other <community, transport-address> pairs to security names * here. For now just let non-IPv4 transport always succeed. * * WHAAAATTTT. No, we don't let non-IPv4 transports * succeed! You must fix this to make it usable, sorry. * From a security standpoint this is insane. -- Wes */ /** @todo alternate com2sec mappings for non v4 transports. Should be implemented via registration */ sn = NULL; } } else #endif /* support for community based SNMP */ if (find_sec_mod(pdu->securityModel)) { /* * any legal defined v3 security model */ DEBUGMSG(("mibII/vacm_vars", "vacm_in_view: ver=%ld, model=%d, secName=%s\n", pdu->version, pdu->securityModel, pdu->securityName)); sn = pdu->securityName; contextName = pdu->contextName; } else { sn = NULL; } if (sn == NULL) { #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) snmp_increment_statistic(STAT_SNMPINBADCOMMUNITYNAMES); #endif DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: No security name found\n")); return VACM_NOSECNAME; } if (pdu->contextNameLen > CONTEXTNAMEINDEXLEN) { DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: bad ctxt length %d\n", (int)pdu->contextNameLen)); return VACM_NOSUCHCONTEXT; } /* * NULL termination of the pdu field is ugly here. Do in PDU parsing? */ if (pdu->contextName) memcpy(contextNameIndex, pdu->contextName, pdu->contextNameLen); else contextNameIndex[0] = '\0'; contextNameIndex[pdu->contextNameLen] = '\0'; if (!(flags & VACM_CHECK_VIEW_CONTENTS_DNE_CONTEXT_OK) && !netsnmp_subtree_find_first(contextNameIndex)) { /* * rfc 3415 section 3.2, step 1 * no such context here; return no such context error */ DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: no such ctxt \"%s\"\n", contextNameIndex)); return VACM_NOSUCHCONTEXT; } DEBUGMSGTL(("mibII/vacm_vars", "vacm_in_view: sn=%s", sn)); gp = vacm_getGroupEntry(pdu->securityModel, sn); if (gp == NULL) { DEBUGMSG(("mibII/vacm_vars", "\n")); return VACM_NOGROUP; } DEBUGMSG(("mibII/vacm_vars", ", gn=%s", gp->groupName)); ap = vacm_getAccessEntry(gp->groupName, contextNameIndex, pdu->securityModel, pdu->securityLevel); if (ap == NULL) { DEBUGMSG(("mibII/vacm_vars", "\n")); return VACM_NOACCESS; } if (name == NULL) { /* only check the setup of the vacm for the request */ DEBUGMSG(("mibII/vacm_vars", ", Done checking setup\n")); return VACM_SUCCESS; } if (viewtype < 0 || viewtype >= VACM_MAX_VIEWS) { DEBUGMSG(("mibII/vacm_vars", " illegal view type\n")); return VACM_NOACCESS; } vn = ap->views[viewtype]; DEBUGMSG(("mibII/vacm_vars", ", vn=%s", vn)); if (check_subtree) { DEBUGMSG(("mibII/vacm_vars", "\n")); return vacm_checkSubtree(vn, name, namelen); } vp = vacm_getViewEntry(vn, name, namelen, VACM_MODE_FIND); if (vp == NULL) { DEBUGMSG(("mibII/vacm_vars", "\n")); return VACM_NOVIEW; } DEBUGMSG(("mibII/vacm_vars", ", vt=%d\n", vp->viewType)); if (vp->viewType == SNMP_VIEW_EXCLUDED) { #if !defined(NETSNMP_DISABLE_SNMPV1) || !defined(NETSNMP_DISABLE_SNMPV2C) #if defined(NETSNMP_DISABLE_SNMPV1) if (pdu->version == SNMP_VERSION_2c) #else #if defined(NETSNMP_DISABLE_SNMPV2C) if (pdu->version == SNMP_VERSION_1) #else if (pdu->version == SNMP_VERSION_1 || pdu->version == SNMP_VERSION_2c) #endif #endif { snmp_increment_statistic(STAT_SNMPINBADCOMMUNITYUSES); } #endif return VACM_NOTINVIEW; } return VACM_SUCCESS; } /* end vacm_in_view() */
int write_mteTriggerDeltaDiscontinuityID(int action, u_char * var_val, u_char var_val_type, size_t var_val_len, u_char * statP, oid * name, size_t name_len) { static oid *tmpvar; struct mteTriggerTable_data *StorageTmp = NULL; static size_t tmplen; size_t newlen = name_len - (sizeof(mteTriggerDeltaTable_variables_oid) / sizeof(oid) + 3 - 1); DEBUGMSGTL(("mteTriggerDeltaTable", "write_mteTriggerDeltaDiscontinuityID entering action=%d... \n", action)); if ((StorageTmp = header_complex(mteTriggerTableStorage, NULL, &name[sizeof(mteTriggerDeltaTable_variables_oid) / sizeof(oid) + 3 - 1], &newlen, 1, NULL, NULL)) == NULL) return SNMP_ERR_NOSUCHNAME; /* remove if you support creation here */ switch (action) { case RESERVE1: if (var_val_type != ASN_OBJECT_ID) { fprintf(stderr, "write to mteTriggerDeltaDiscontinuityID not ASN_OBJECT_ID\n"); return SNMP_ERR_WRONGTYPE; } if (StorageTmp->storageType != ST_NONVOLATILE) return SNMP_ERR_NOTWRITABLE; break; case RESERVE2: /* * memory reseveration, final preparation... */ break; case FREE: /* * Release any resources that have been allocated */ break; case ACTION: /* * The variable has been stored in objid for * you to use, and you have just been asked to do something with * it. Note that anything done here must be reversable in the UNDO case */ tmpvar = StorageTmp->mteTriggerDeltaDiscontinuityID; tmplen = StorageTmp->mteTriggerDeltaDiscontinuityIDLen; memdup((u_char **) & StorageTmp->mteTriggerDeltaDiscontinuityID, var_val, var_val_len); StorageTmp->mteTriggerDeltaDiscontinuityIDLen = var_val_len / sizeof(oid); break; case UNDO: /* * Back out any changes made in the ACTION case */ SNMP_FREE(StorageTmp->mteTriggerDeltaDiscontinuityID); StorageTmp->mteTriggerDeltaDiscontinuityID = tmpvar; StorageTmp->mteTriggerDeltaDiscontinuityIDLen = tmplen; break; case COMMIT: /* * Things are working well, so it's now safe to make the change * permanently. Make sure that anything done here can't fail! */ SNMP_FREE(tmpvar); break; } return SNMP_ERR_NOERROR; }
static int netsnmp_tlstcp_recv(netsnmp_transport *t, void *buf, int size, void **opaque, int *olength) { int rc = -1; netsnmp_indexed_addr_pair *addr_pair = NULL; struct sockaddr *from; netsnmp_tmStateReference *tmStateRef = NULL; _netsnmpTLSBaseData *tlsdata; if (NULL == t || t->sock < 0 || NULL == t->data) { snmp_log(LOG_ERR, "tlstcp received an invalid invocation with missing data\n"); DEBUGMSGTL(("tlstcp", "recvfrom fd %d err %d (\"%s\")\n", t->sock, errno, strerror(errno))); DEBUGMSGTL(("tlstcp", " tdata = %p\n", t->data)); return -1; } /* RFC5953 Section 5.1.2 step 1: 1) Determine the tlstmSessionID for the incoming message. The tlstmSessionID MUST be a unique session identifier for this (D)TLS connection. The contents and format of this identifier are implementation-dependent as long as it is unique to the session. A session identifier MUST NOT be reused until all references to it are no longer in use. The tmSessionID is equal to the tlstmSessionID discussed in Section 5.1.1. tmSessionID refers to the session identifier when stored in the tmStateReference and tlstmSessionID refers to the session identifier when stored in the LCD. They MUST always be equal when processing a given session's traffic. */ /* For this implementation we use the t->data memory pointer as the sessionID. As it's a pointer to session specific data tied with the transport object we know it'll never be realloated (ie, duplicated) until release by this transport object and is safe to use as a unique session identifier. */ tlsdata = t->data; if (NULL == tlsdata->ssl) { snmp_log(LOG_ERR, "tlstcp received an invalid invocation without ssl data\n"); return -1; } /* RFC5953 Section 5.1.2 step 1, part2: * This part (incrementing the counter) is done in the netsnmp_tlstcp_accept function. */ /* RFC5953 Section 5.1.2 step 2: * Create a tmStateReference cache for the subsequent reference and assign the following values within it: tmTransportDomain = snmpTLSTCPDomain or snmpDTLSUDPDomain as appropriate. tmTransportAddress = The address the message originated from. tmSecurityLevel = The derived tmSecurityLevel for the session, as discussed in Section 3.1.2 and Section 5.3. tmSecurityName = The fderived tmSecurityName for the session as discussed in Section 5.3. This value MUST be constant during the lifetime of the session. tmSessionID = The tlstmSessionID described in step 1 above. */ /* Implementation notes: * - The tmTransportDomain is represented by the transport object * - The tmpSessionID is represented by the tlsdata pointer (as discussed above) * - The following items are handled later in netsnmp_tlsbase_wrapup_recv: - tmSecurityLevel - tmSecurityName - tmSessionID */ /* create a tmStateRef cache for slow fill-in */ tmStateRef = SNMP_MALLOC_TYPEDEF(netsnmp_tmStateReference); if (tmStateRef == NULL) { *opaque = NULL; *olength = 0; return -1; } /* Set the transportDomain */ memcpy(tmStateRef->transportDomain, netsnmpTLSTCPDomain, sizeof(netsnmpTLSTCPDomain[0]) * netsnmpTLSTCPDomain_len); tmStateRef->transportDomainLen = netsnmpTLSTCPDomain_len; /* Set the tmTransportAddress */ addr_pair = &tmStateRef->addresses; tmStateRef->have_addresses = 1; from = (struct sockaddr *) &(addr_pair->remote_addr); /* RFC5953 Section 5.1.2 step 1: * 3) The incomingMessage and incomingMessageLength are assigned values from the (D)TLS processing. */ /* Implementation notes: - incomingMessage = buf pointer - incomingMessageLength = rc */ /* read the packet from openssl */ rc = SSL_read(tlsdata->ssl, buf, size); while (rc <= 0) { if (rc == 0) { /* XXX closed connection */ DEBUGMSGTL(("tlstcp", "remote side closed connection\n")); /* XXX: openssl cleanup */ SNMP_FREE(tmStateRef); return -1; } rc = SSL_read(tlsdata->ssl, buf, size); } DEBUGMSGTL(("tlstcp", "received %d decoded bytes from tls\n", rc)); /* Check for errors */ if (rc == -1) { if (SSL_get_error(tlsdata->ssl, rc) == SSL_ERROR_WANT_READ) return -1; /* XXX: it's ok, but what's the right return? */ _openssl_log_error(rc, tlsdata->ssl, "SSL_read"); SNMP_FREE(tmStateRef); return rc; } /* log the packet */ { char *str = netsnmp_tlstcp_fmtaddr(t, NULL, 0); DEBUGMSGTL(("tlstcp", "recvfrom fd %d got %d bytes (from %s)\n", t->sock, rc, str)); free(str); } /* Other wrap-up things common to TLS and DTLS */ if (netsnmp_tlsbase_wrapup_recv(tmStateRef, tlsdata, opaque, olength) != SNMPERR_SUCCESS) return SNMPERR_GENERR; /* RFC5953 Section 5.1.2 step 1: * 4) The TLS Transport Model passes the transportDomain, transportAddress, incomingMessage, and incomingMessageLength to the Dispatcher using the receiveMessage ASI: */ /* In our implementation, this is done simply by returning */ return rc; }
netsnmp_transport * netsnmp_tlstcp_open(netsnmp_transport *t) { _netsnmpTLSBaseData *tlsdata; BIO *bio; SSL_CTX *ctx; SSL *ssl; int rc = 0; _netsnmp_verify_info *verify_info; netsnmp_assert_or_return(t != NULL, NULL); netsnmp_assert_or_return(t->data != NULL, NULL); netsnmp_assert_or_return(sizeof(_netsnmpTLSBaseData) == t->data_length, NULL); tlsdata = t->data; if (tlsdata->flags & NETSNMP_TLSBASE_IS_CLIENT) { /* Is the client */ /* RFC5953 Section 5.3.1: Establishing a Session as a Client * 1) The snmpTlstmSessionOpens counter is incremented. */ snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENS); /* RFC5953 Section 5.3.1: Establishing a Session as a Client 2) The client selects the appropriate certificate and cipher_suites for the key agreement based on the tmSecurityName and the tmRequestedSecurityLevel for the session. For sessions being established as a result of a SNMP-TARGET-MIB based operation, the certificate will potentially have been identified via the snmpTlstmParamsTable mapping and the cipher_suites will have to be taken from system-wide or implementation-specific configuration. If no row in the snmpTlstmParamsTable exists then implementations MAY choose to establish the connection using a default client certificate available to the application. Otherwise, the certificate and appropriate cipher_suites will need to be passed to the openSession() ASI as supplemental information or configured through an implementation-dependent mechanism. It is also implementation-dependent and possibly policy-dependent how tmRequestedSecurityLevel will be used to influence the security capabilities provided by the (D)TLS connection. However this is done, the security capabilities provided by (D)TLS MUST be at least as high as the level of security indicated by the tmRequestedSecurityLevel parameter. The actual security level of the session is reported in the tmStateReference cache as tmSecurityLevel. For (D)TLS to provide strong authentication, each principal acting as a command generator SHOULD have its own certificate. */ /* Implementation notes: we do most of this in the sslctx_client_setup The transport should have been f_config()ed with the proper fingerprints to use (which is stored in tlsdata), or we'll use the default identity fingerprint if that can be found. */ /* XXX: check securityLevel and ensure no NULL fingerprints are used */ /* set up the needed SSL context */ tlsdata->ssl_context = ctx = sslctx_client_setup(TLSv1_method(), tlsdata); if (!ctx) { snmp_log(LOG_ERR, "failed to create TLS context\n"); return NULL; } /* RFC5953 Section 5.3.1: Establishing a Session as a Client 3) Using the destTransportDomain and destTransportAddress values, the client will initiate the (D)TLS handshake protocol to establish session keys for message integrity and encryption. */ /* Implementation note: The transport domain and address are pre-processed by this point */ /* Create a BIO connection for it */ DEBUGMSGTL(("tlstcp", "connecting to tlstcp %s\n", tlsdata->addr_string)); t->remote = (void *) strdup(tlsdata->addr_string); t->remote_length = strlen(tlsdata->addr_string) + 1; bio = BIO_new_connect(tlsdata->addr_string); /* RFC5953 Section 5.3.1: Establishing a Session as a Client 3) continued: If the attempt to establish a session is unsuccessful, then snmpTlstmSessionOpenErrors is incremented, an error indication is returned, and processing stops. */ if (NULL == bio) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to create bio\n"); _openssl_log_error(rc, NULL, "BIO creation"); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* Tell the BIO to actually do the connection */ if ((rc = BIO_do_connect(bio)) <= 0) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to connect to %s\n", tlsdata->addr_string); _openssl_log_error(rc, NULL, "BIO_do_connect"); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* Create the SSL layer on top of the socket bio */ ssl = tlsdata->ssl = SSL_new(ctx); if (NULL == ssl) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n"); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* Bind the SSL layer to the BIO */ SSL_set_bio(ssl, bio, bio); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); verify_info = SNMP_MALLOC_TYPEDEF(_netsnmp_verify_info); if (NULL == verify_info) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to create a SSL connection\n"); SSL_shutdown(ssl); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } SSL_set_ex_data(ssl, tls_get_verify_info_index(), verify_info); /* Then have SSL do it's connection over the BIO */ if ((rc = SSL_connect(ssl)) <= 0) { snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONOPENERRORS); snmp_log(LOG_ERR, "tlstcp: failed to ssl_connect\n"); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* RFC5953 Section 5.3.1: Establishing a Session as a Client 3) continued: If the session failed to open because the presented server certificate was unknown or invalid then the snmpTlstmSessionUnknownServerCertificate or snmpTlstmSessionInvalidServerCertificates MUST be incremented and a snmpTlstmServerCertificateUnknown or snmpTlstmServerInvalidCertificate notification SHOULD be sent as appropriate. Reasons for server certificate invalidation includes, but is not limited to, cryptographic validation failures and an unexpected presented certificate identity. */ /* RFC5953 Section 5.3.1: Establishing a Session as a Client 4) The (D)TLS client MUST then verify that the (D)TLS server's presented certificate is the expected certificate. The (D)TLS client MUST NOT transmit SNMP messages until the server certificate has been authenticated, the client certificate has been transmitted and the TLS connection has been fully established. If the connection is being established from configuration based on SNMP-TARGET-MIB configuration, then the snmpTlstmAddrTable DESCRIPTION clause describes how the verification is done (using either a certificate fingerprint, or an identity authenticated via certification path validation). If the connection is being established for reasons other than configuration found in the SNMP-TARGET-MIB then configuration and procedures outside the scope of this document should be followed. Configuration mechanisms SHOULD be similar in nature to those defined in the snmpTlstmAddrTable to ensure consistency across management configuration systems. For example, a command-line tool for generating SNMP GETs might support specifying either the server's certificate fingerprint or the expected host name as a command line argument. */ /* Implementation notes: - All remote certificate fingerprints are expected to be stored in the transport's config information. This is true both for CLI clients and TARGET-MIB sessions. - netsnmp_tlsbase_verify_server_cert implements these checks */ if (netsnmp_tlsbase_verify_server_cert(ssl, tlsdata) != SNMPERR_SUCCESS) { /* XXX: unknown vs invalid; two counters */ snmp_increment_statistic(STAT_TLSTM_SNMPTLSTMSESSIONUNKNOWNSERVERCERTIFICATE); snmp_log(LOG_ERR, "tlstcp: failed to verify ssl certificate\n"); SSL_shutdown(ssl); BIO_free(bio); SNMP_FREE(tlsdata); SNMP_FREE(t); return NULL; } /* RFC5953 Section 5.3.1: Establishing a Session as a Client 5) (D)TLS provides assurance that the authenticated identity has been signed by a trusted configured certification authority. If verification of the server's certificate fails in any way (for example because of failures in cryptographic verification or the presented identity did not match the expected named entity) then the session establishment MUST fail, the snmpTlstmSessionInvalidServerCertificates object is incremented. If the session can not be opened for any reason at all, including cryptographic verification failures, then the snmpTlstmSessionOpenErrors counter is incremented and processing stops. */ /* XXX: add snmpTlstmSessionInvalidServerCertificates on crypto failure */ /* RFC5953 Section 5.3.1: Establishing a Session as a Client 6) The TLSTM-specific session identifier (tlstmSessionID) is set in the tmSessionID of the tmStateReference passed to the TLS Transport Model to indicate that the session has been established successfully and to point to a specific (D)TLS connection for future use. The tlstmSessionID is also stored in the LCD for later lookup during processing of incoming messages (Section 5.1.2). */ /* Implementation notes: - the tlsdata pointer is used as our session identifier, as noted in the netsnmp_tlstcp_recv() function comments. */ t->sock = BIO_get_fd(bio, NULL); } else { /* Is the server */ /* Create the socket bio */ DEBUGMSGTL(("tlstcp", "listening on tlstcp port %s\n", tlsdata->addr_string)); tlsdata->accept_bio = BIO_new_accept(tlsdata->addr_string); t->local = (void *) strdup(tlsdata->addr_string); t->local_length = strlen(tlsdata->addr_string)+1; if (NULL == tlsdata->accept_bio) { SNMP_FREE(t); SNMP_FREE(tlsdata); snmp_log(LOG_ERR, "TLSTCP: Falied to create a accept BIO\n"); return NULL; } /* openssl requires an initial accept to bind() the socket */ if (BIO_do_accept(tlsdata->accept_bio) <= 0) { _openssl_log_error(rc, tlsdata->ssl, "BIO_do__accept"); SNMP_FREE(t); SNMP_FREE(tlsdata); snmp_log(LOG_ERR, "TLSTCP: Falied to do first accept on the TLS accept BIO\n"); return NULL; } /* create the OpenSSL TLS context */ tlsdata->ssl_context = sslctx_server_setup(TLSv1_method()); t->sock = BIO_get_fd(tlsdata->accept_bio, NULL); t->flags |= NETSNMP_TRANSPORT_FLAG_LISTEN; } return t; }
_KEYTOOLS_NOT_AVAILABLE #endif /* internal or openssl */ /*******************************************************************-o-****** * decode_keychange * * Parameters: * *hashtype MIB OID of the hash transform to use. * hashtype_len Length of the hash transform MIB OID. * *oldkey Old key that is used to encode the new key. * oldkey_len Length of oldkey in bytes. * *kcstring Encoded KeyString buffer containing the new key. * kcstring_len Length of kcstring in bytes. * *newkey Buffer to hold the extracted new key. * *newkey_len Length of newkey in bytes. * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_GENERR All errors. * * * Decodes a string of bits encoded according to the KeyChange TC described * in RFC 2274, Section 5. The new key is extracted from *kcstring with * the aid of the old key. * * Upon successful return, *newkey_len contains the length of the new key. * * * ASSUMES Old key is exactly 1/2 the length of the KeyChange buffer, * although this length may be less than the hash transform * output. Thus the new key length will be equal to the old * key length. */ /* XXX: if the newkey is not long enough, it should be freed and remalloced */ int decode_keychange( oid *hashtype, u_int hashtype_len, u_char *oldkey, size_t oldkey_len, u_char *kcstring, size_t kcstring_len, u_char *newkey, size_t *newkey_len) #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) { int rval = SNMPERR_SUCCESS; size_t properlength = 0; u_int nbytes = 0; u_char *bufp, tmp_buf[SNMP_MAXBUF]; size_t tmp_buf_len = SNMP_MAXBUF; void *context = NULL; u_char *tmpbuf = NULL; /* * Sanity check. */ if ( !hashtype || !oldkey || !kcstring || !newkey || !newkey_len || (oldkey_len<=0) || (kcstring_len<=0) || (*newkey_len<=0) || (hashtype_len != USM_LENGTH_OID_TRANSFORM) ) { QUITFUN(SNMPERR_GENERR, decode_keychange_quit); } /* * Setup for the transform type. */ properlength = sc_get_properlength(hashtype, hashtype_len); if (properlength == SNMPERR_GENERR) QUITFUN(SNMPERR_GENERR, decode_keychange_quit); if ( ((oldkey_len*2) != kcstring_len) || (*newkey_len < oldkey_len) ) { QUITFUN(SNMPERR_GENERR, decode_keychange_quit); } properlength = oldkey_len; *newkey_len = properlength; /* * Use the old key and the given KeyChange TC string to recover * the new key: * . Hash (oldkey | random_bytes) (into newkey), * . XOR hash and encoded (second) half of kcstring (into newkey). */ tmpbuf = (u_char *)malloc(properlength*2); if (tmpbuf) { memcpy(tmpbuf, oldkey, properlength); memcpy(tmpbuf+properlength, kcstring, properlength); rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength*2, tmp_buf, &tmp_buf_len); QUITFUN(rval, decode_keychange_quit); memcpy(newkey, tmp_buf, properlength); bufp = kcstring+properlength; nbytes = 0; while ((int)(nbytes++) < properlength) { *newkey++ = *newkey ^ *bufp++; } } decode_keychange_quit: if (rval != SNMPERR_SUCCESS) { memset(newkey, 0, properlength); } memset(tmp_buf, 0, SNMP_MAXBUF); SNMP_FREE(context); if (tmpbuf != NULL) SNMP_FREE(tmpbuf); return rval; } /* end decode_keychange() */
/** Initialize the LHAMembershipTable table by defining its contents and how it's structured */ void initialize_table_LHAMembershipTable(void) { static oid LHAMembershipTable_oid[] = {1,3,6,1,4,1,4682,6}; netsnmp_table_registration_info *table_info; netsnmp_handler_registration *my_handler; netsnmp_iterator_info *iinfo; /** create the table registration information structures */ table_info = SNMP_MALLOC_TYPEDEF(netsnmp_table_registration_info); iinfo = SNMP_MALLOC_TYPEDEF(netsnmp_iterator_info); /** if your table is read only, it's easiest to change the HANDLER_CAN_RWRITE definition below to HANDLER_CAN_RONLY */ my_handler = netsnmp_create_handler_registration("LHAMembershipTable", LHAMembershipTable_handler, LHAMembershipTable_oid, OID_LENGTH(LHAMembershipTable_oid), HANDLER_CAN_RONLY); if (!my_handler || !table_info || !iinfo) { snmp_log(LOG_ERR, "malloc failed in initialize_table_LHAMembershipTable"); SNMP_FREE(iinfo); SNMP_FREE(table_info); return; /* Serious error. */ } /*************************************************** * Setting up the table's definition */ netsnmp_table_helper_add_indexes(table_info, ASN_INTEGER, /* index: LHAMemberIndex */ 0); /** Define the minimum and maximum accessible columns. This optimizes retrival. */ table_info->min_column = 2; table_info->max_column = 7; /* iterator access routines */ iinfo->get_first_data_point = LHAMembershipTable_get_first_data_point; iinfo->get_next_data_point = LHAMembershipTable_get_next_data_point; /** tie the two structures together */ iinfo->table_reginfo = table_info; /*************************************************** * registering the table with the master agent */ DEBUGMSGTL(("initialize_table_LHAMembershipTable", "Registering table LHAMembershipTable as a table iterator\n")); netsnmp_register_table_iterator(my_handler, iinfo); /* * .... with a local cache */ netsnmp_inject_handler(my_handler, netsnmp_get_cache_handler(snmp_cache_time_out, LHAMembershipTable_load, LHAMembershipTable_free, LHAMembershipTable_oid, OID_LENGTH(LHAMembershipTable_oid))); }
_KEYTOOLS_NOT_AVAILABLE #endif /* internal or openssl */ /*******************************************************************-o-****** * encode_keychange * * Parameters: * *hashtype MIB OID for the hash transform type. * hashtype_len Length of the MIB OID hash transform type. * *oldkey Old key that is used to encodes the new key. * oldkey_len Length of oldkey in bytes. * *newkey New key that is encoded using the old key. * newkey_len Length of new key in bytes. * *kcstring Buffer to contain the KeyChange TC string. * *kcstring_len Length of kcstring buffer. * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_GENERR All errors. * * * Uses oldkey and acquired random bytes to encode newkey into kcstring * according to the rules of the KeyChange TC described in RFC 2274, Section 5. * * Upon successful return, *kcstring_len contains the length of the * encoded string. * * ASSUMES Old and new key are always equal to each other, although * this may be less than the transform type hash output * output length (eg, using KeyChange for a DESPriv key when * the user also uses SHA1Auth). This also implies that the * hash placed in the second 1/2 of the key change string * will be truncated before the XOR'ing when the hash output is * larger than that 1/2 of the key change string. * * *kcstring_len will be returned as exactly twice that same * length though the input buffer may be larger. * * XXX FIX: Does not handle varibable length keys. * XXX FIX: Does not handle keys larger than the hash algorithm used. */ int encode_keychange( oid *hashtype, u_int hashtype_len, u_char *oldkey, size_t oldkey_len, u_char *newkey, size_t newkey_len, u_char *kcstring, size_t *kcstring_len) #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) { int rval = SNMPERR_SUCCESS; size_t properlength; size_t nbytes = 0; u_char *tmpbuf = NULL; void *context = NULL; /* * Sanity check. */ if ( !hashtype || !oldkey || !newkey || !kcstring || !kcstring_len || (oldkey_len<=0) || (newkey_len<=0) || (*kcstring_len<=0) || (hashtype_len != USM_LENGTH_OID_TRANSFORM) ) { QUITFUN(SNMPERR_GENERR, encode_keychange_quit); } /* * Setup for the transform type. */ properlength = sc_get_properlength(hashtype, hashtype_len); if (properlength == SNMPERR_GENERR) QUITFUN(SNMPERR_GENERR, encode_keychange_quit); if ( (oldkey_len != newkey_len) || (*kcstring_len < (2*oldkey_len)) ) { QUITFUN(SNMPERR_GENERR, encode_keychange_quit); } properlength = SNMP_MIN((int)oldkey_len, properlength); /* * Use the old key and some random bytes to encode the new key * in the KeyChange TC format: * . Get random bytes (store in first half of kcstring), * . Hash (oldkey | random_bytes) (into second half of kcstring), * . XOR hash and newkey (into second half of kcstring). * * Getting the wrong number of random bytes is considered an error. */ nbytes = properlength; #if defined(SNMP_TESTING_CODE) && defined(RANDOMZEROS) memset(kcstring, 0, nbytes); DEBUGMSG(("encode_keychange", "** Using all zero bits for \"random\" delta of )" "the keychange string! **\n")); #else /* !SNMP_TESTING_CODE */ rval = sc_random(kcstring, &nbytes); QUITFUN(rval, encode_keychange_quit); if ((int)nbytes != properlength) { QUITFUN(SNMPERR_GENERR, encode_keychange_quit); } #endif /* !SNMP_TESTING_CODE */ tmpbuf = (u_char *)malloc(properlength*2); if (tmpbuf) { memcpy(tmpbuf, oldkey, properlength); memcpy(tmpbuf+properlength, kcstring, properlength); *kcstring_len -= properlength; rval = sc_hash(hashtype, hashtype_len, tmpbuf, properlength*2, kcstring+properlength, kcstring_len); QUITFUN(rval, encode_keychange_quit); *kcstring_len = (properlength*2); kcstring += properlength; nbytes = 0; while ((int)(nbytes++) < properlength) { *kcstring++ = *kcstring ^ *newkey++; } } encode_keychange_quit: if (rval != SNMPERR_SUCCESS) memset(kcstring, 0, *kcstring_len); SNMP_FREE(tmpbuf); SNMP_FREE(context); return rval; } /* end encode_keychange() */
_KEYTOOLS_NOT_AVAILABLE #endif /* internal or openssl */ /*******************************************************************-o-****** * generate_kul * * Parameters: * *hashtype * hashtype_len * *engineID * engineID_len * *Ku Master key for a given user. * ku_len Length of Ku in bytes. * *Kul Localized key for a given user at engineID. * *kul_len Length of Kul buffer (IN); Length of Kul key (OUT). * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_GENERR All errors. * * * Ku MUST be the proper length (currently fixed) for the given hashtype. * * Upon successful return, Kul contains the localized form of Ku at * engineID, and the length of the key is stored in kul_len. * * The localized key method is defined in RFC2274, Sections 2.6 and A.2, and * originally documented in: * U. Blumenthal, N. C. Hien, B. Wijnen, * "Key Derivation for Network Management Applications", * IEEE Network Magazine, April/May issue, 1997. * * * ASSUMES SNMP_MAXBUF >= sizeof(Ku + engineID + Ku). * * NOTE Localized keys for privacy transforms are generated via * the authentication transform held by the same usmUser. * * XXX An engineID of any length is accepted, even if larger than * what is spec'ed for the textual convention. */ int generate_kul( oid *hashtype, u_int hashtype_len, u_char *engineID, size_t engineID_len, u_char *Ku, size_t ku_len, u_char *Kul, size_t *kul_len) #if defined(USE_OPENSSL) || defined(USE_INTERNAL_MD5) { int rval = SNMPERR_SUCCESS; u_int nbytes = 0; size_t properlength; u_char buf[SNMP_MAXBUF]; void *context = NULL; #ifdef SNMP_TESTING_CODE int i; #endif /* * Sanity check. */ if ( !hashtype || !engineID || !Ku || !Kul || !kul_len || (engineID_len<=0) || (ku_len<=0) || (*kul_len<=0) || (hashtype_len != USM_LENGTH_OID_TRANSFORM) ) { QUITFUN(SNMPERR_GENERR, generate_kul_quit); } properlength = sc_get_properlength(hashtype, hashtype_len); if (properlength == SNMPERR_GENERR) QUITFUN(SNMPERR_GENERR, generate_kul_quit); if (((int)*kul_len < properlength) || ((int)ku_len < properlength) ) { QUITFUN(SNMPERR_GENERR, generate_kul_quit); } /* * Concatenate Ku and engineID properly, then hash the result. * Store it in Kul. */ nbytes = 0; memcpy(buf, Ku, properlength); nbytes += properlength; memcpy(buf+nbytes, engineID, engineID_len); nbytes += engineID_len; memcpy(buf+nbytes, Ku, properlength); nbytes += properlength; rval = sc_hash(hashtype, hashtype_len, buf, nbytes, Kul, kul_len); #ifdef SNMP_TESTING_CODE DEBUGMSGTL(("generate_kul", "generating Kul (from Ku): ")); for(i=0; i < *kul_len; i++) DEBUGMSG(("generate_kul", "%02x",Kul[i])); DEBUGMSG(("generate_kul", "keytools\n")); #endif /* SNMP_TESTING_CODE */ QUITFUN(rval, generate_kul_quit); generate_kul_quit: SNMP_FREE(context); return rval; } /* end generate_kul() */
static int netsnmp_dtlsudp_recv(netsnmp_transport *t, void *buf, int size, void **opaque, int *olength) { int rc = -1; socklen_t fromlen = sizeof(struct sockaddr); netsnmp_addr_pair *addr_pair = NULL; struct sockaddr *from; netsnmp_tmStateReference *tmStateRef = NULL; X509 *peer; if (t != NULL && t->sock >= 0) { /* create a tmStateRef cache for slow fill-in */ tmStateRef = SNMP_MALLOC_TYPEDEF(netsnmp_tmStateReference); if (tmStateRef == NULL) { *opaque = NULL; *olength = 0; return -1; } addr_pair = &tmStateRef->addresses; tmStateRef->have_addresses = 1; from = (struct sockaddr *) &(addr_pair->remote_addr); while (rc < 0) { #if defined(linux) && defined(IP_PKTINFO) rc = netsnmp_udp_recvfrom(t->sock, buf, size, from, &fromlen, &(addr_pair->local_addr)); #else rc = recvfrom(t->sock, buf, size, NETSNMP_DONTWAIT, from, &fromlen); #endif /* linux && IP_PKTINFO */ if (rc < 0 && errno != EINTR) { break; } } DEBUGMSGTL(("dtlsudp", "received %d raw bytes on way to dtls\n", rc)); if (rc < 0) { DEBUGMSGTL(("dtlsudp", "recvfrom fd %d err %d (\"%s\")\n", t->sock, errno, strerror(errno))); SNMP_FREE(tmStateRef); return -1; } if (rc >= 0) { /* now that we have the from address filled in, we can look up the openssl context and have openssl read and process appropriately */ /* if we don't have a cachep for this connection then we're receiving something new and are the server side */ /* XXX: allow for a SNMP client to never accept new conns? */ bio_cache *cachep = find_or_create_bio_cache(t->sock, &addr_pair->remote_addr, WE_ARE_SERVER); if (NULL == cachep) { SNMP_FREE(tmStateRef); return -1; } /* write the received buffer to the memory-based input bio */ BIO_write(cachep->bio, buf, rc); /* XXX: in Wes' other example we do a SSL_pending() call too to ensure we're ready to read... it's possible that buffered stuff in openssl won't be caught by the net-snmp select loop because it's already been pulled out; need to deal with this) */ rc = SSL_read(cachep->con, buf, size); DEBUGMSGTL(("dtlsudp", "received %d decoded bytes from dtls\n", rc)); if (BIO_ctrl_pending(cachep->write_bio) > 0) { /* we have outgoing data to send; probably DTLS negotation */ u_char outbuf[65535]; int outsize; int rc2; /* for memory bios, we now read from openssl's write buffer (ie, the packet to go out) and send it out the udp port manually */ outsize = BIO_read(cachep->write_bio, outbuf, sizeof(outbuf)); if (outsize > 0) { /* should always be true. */ #if defined(XXXFIXME) && defined(linux) && defined(IP_PKTINFO) /* XXX: before this can work, we need to remember address we received it from (addr_pair) */ rc2 = netsnmp_udp_sendto(cachep->sock, addr_pair->local_addr, addr_pair->remote_addr, outbuf, outsize); #else rc2 = sendto(t->sock, outbuf, outsize, 0, &cachep->sockaddr, sizeof(struct sockaddr)); #endif /* linux && IP_PKTINFO */ if (rc2 == -1) { snmp_log(LOG_ERR, "failed to send a DTLS specific packet\n"); } } } if (SSL_pending(cachep->con)) { fprintf(stderr, "ack: got here... pending\n"); exit(1); } if (rc == -1) { _openssl_log_error(rc, cachep->con, "SSL_read"); SNMP_FREE(tmStateRef); if (SSL_get_error(cachep->con, rc) == SSL_ERROR_WANT_READ) return -1; /* XXX: it's ok, but what's the right return? */ return rc; } { char *str = netsnmp_udp_fmtaddr(NULL, addr_pair, sizeof(netsnmp_addr_pair)); DEBUGMSGTL(("dtlsudp", "recvfrom fd %d got %d bytes (from %s)\n", t->sock, rc, str)); free(str); } /* XXX: disallow NULL auth/encr algs in our implementations */ tmStateRef->transportSecurityLevel = SNMP_SEC_LEVEL_AUTHPRIV; /* use x509 cert to do lookup to secname if DNE in cachep yet */ if (!cachep->securityName) { if (NULL != (peer = SSL_get_peer_certificate(cachep->con))) { X509_NAME *subname; char namebuf[1024]; /* we have one */ subname = X509_get_subject_name(peer); X509_NAME_get_text_by_NID(subname, NID_commonName, namebuf, sizeof(namebuf)); DEBUGMSGTL(("dtlsudp", "got commonname: %s\n", namebuf)); cachep->securityName = strdup(namebuf); DEBUGMSGTL(("dtlsudp", "set SecName to: %s\n", cachep->securityName)); } else { SNMP_FREE(tmStateRef); return -1; } } /* XXX: detect and throw out overflow secname sizes rather than truncating. */ strncpy(tmStateRef->securityName, cachep->securityName, sizeof(tmStateRef->securityName)-1); tmStateRef->securityName[sizeof(tmStateRef->securityName)-1] = '\0'; tmStateRef->securityNameLen = strlen(tmStateRef->securityName); *opaque = tmStateRef; *olength = sizeof(netsnmp_tmStateReference); } else { DEBUGMSGTL(("dtlsudp", "recvfrom fd %d err %d (\"%s\")\n", t->sock, errno, strerror(errno))); } } return rc; }
/*******************************************************************-o-****** * setup_engineID * * Parameters: * **eidp * *text Printable (?) text to be plugged into the snmpEngineID. * * Return: * Length of allocated engineID string in bytes, -OR- * -1 on error. * * * Create an snmpEngineID using text and the local IP address. If eidp * is defined, use it to return a pointer to the newly allocated data. * Otherwise, use the result to define engineID defined in this module. * * Line syntax: * engineID <text> | NULL * * XXX What if a node has multiple interfaces? * XXX What if multiple engines all choose the same address? * (answer: You're screwed, because you might need a kul database * which is dependant on the current engineID. Enumeration and other * tricks won't work). */ int setup_engineID(u_char ** eidp, const char *text) { int enterpriseid = htonl(ENTERPRISE_OID), netsnmpoid = htonl(NETSNMP_OID), localsetup = (eidp) ? 0 : 1; /* * Use local engineID if *eidp == NULL. */ #ifdef HAVE_GETHOSTNAME u_char buf[SNMP_MAXBUF_SMALL]; struct hostent *hent = NULL; #endif u_char *bufp = NULL; size_t len; int localEngineIDType = engineIDType; int tmpint; time_t tmptime; engineIDIsSet = 1; #ifdef HAVE_GETHOSTNAME #ifdef AF_INET6 /* * see if they selected IPV4 or IPV6 support */ if ((ENGINEID_TYPE_IPV6 == localEngineIDType) || (ENGINEID_TYPE_IPV4 == localEngineIDType)) { /* * get the host name and save the information */ gethostname((char *) buf, sizeof(buf)); hent = gethostbyname((char *) buf); if (hent && hent->h_addrtype == AF_INET6) { localEngineIDType = ENGINEID_TYPE_IPV6; } else { /* * Not IPV6 so we go with default */ localEngineIDType = ENGINEID_TYPE_IPV4; } } #else /* * No IPV6 support. Check if they selected IPV6 engineID type. * If so make it IPV4 instead */ if (ENGINEID_TYPE_IPV6 == localEngineIDType) { localEngineIDType = ENGINEID_TYPE_IPV4; } if (ENGINEID_TYPE_IPV4 == localEngineIDType) { /* * get the host name and save the information */ gethostname((char *) buf, sizeof(buf)); hent = gethostbyname((char *) buf); } #endif #endif /* HAVE_GETHOSTNAME */ /* * Determine if we have text and if so setup our localEngineIDType * * appropriately. */ if (NULL != text) { engineIDType = localEngineIDType = ENGINEID_TYPE_TEXT; } /* * Determine length of the engineID string. */ len = 5; /* always have 5 leading bytes */ switch (localEngineIDType) { case ENGINEID_TYPE_TEXT: len += strlen(text); /* 5 leading bytes+text. No NULL char */ break; #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) case ENGINEID_TYPE_MACADDR: /* MAC address */ len += 6; /* + 6 bytes for MAC address */ break; #endif case ENGINEID_TYPE_IPV4: /* IPv4 */ len += 4; /* + 4 byte IPV4 address */ break; case ENGINEID_TYPE_IPV6: /* IPv6 */ len += 16; /* + 16 byte IPV6 address */ break; case ENGINEID_TYPE_NETSNMP_RND: /* Net-SNMP specific encoding */ if (engineID) /* already setup, keep current value */ return engineIDLength; if (oldEngineID) { len = oldEngineIDLength; } else { len += sizeof(int) + sizeof(time_t); } break; default: snmp_log(LOG_ERR, "Unknown EngineID type requested for setup (%d). Using IPv4.\n", localEngineIDType); localEngineIDType = ENGINEID_TYPE_IPV4; /* make into IPV4 */ len += 4; /* + 4 byte IPv4 address */ break; } /* switch */ /* * Allocate memory and store enterprise ID. */ if ((bufp = (u_char *) malloc(len)) == NULL) { snmp_log_perror("setup_engineID malloc"); return -1; } if (localEngineIDType == ENGINEID_TYPE_NETSNMP_RND) /* * we must use the net-snmp enterprise id here, regardless */ memcpy(bufp, &netsnmpoid, sizeof(netsnmpoid)); /* XXX Must be 4 bytes! */ else memcpy(bufp, &enterpriseid, sizeof(enterpriseid)); /* XXX Must be 4 bytes! */ bufp[0] |= 0x80; /* * Store the given text -OR- the first found IP address * -OR- the MAC address -OR- random elements * (the latter being the recommended default) */ switch (localEngineIDType) { case ENGINEID_TYPE_NETSNMP_RND: if (oldEngineID) { /* * keep our previous notion of the engineID */ memcpy(bufp, oldEngineID, oldEngineIDLength); } else { /* * Here we've desigend our own ENGINEID that is not based on * an address which may change and may even become conflicting * in the future like most of the default v3 engineID types * suffer from. * * Ours is built from 2 fairly random elements: a random number and * the current time in seconds. This method suffers from boxes * that may not have a correct clock setting and random number * seed at startup, but few OSes should have that problem. */ bufp[4] = ENGINEID_TYPE_NETSNMP_RND; tmpint = random(); memcpy(bufp + 5, &tmpint, sizeof(tmpint)); tmptime = time(NULL); memcpy(bufp + 5 + sizeof(tmpint), &tmptime, sizeof(tmptime)); } break; case ENGINEID_TYPE_TEXT: bufp[4] = ENGINEID_TYPE_TEXT; memcpy((char *) bufp + 5, text, strlen(text)); break; #ifdef HAVE_GETHOSTNAME #ifdef AF_INET6 case ENGINEID_TYPE_IPV6: bufp[4] = ENGINEID_TYPE_IPV6; memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length); break; #endif #endif #if defined(IFHWADDRLEN) && defined(SIOCGIFHWADDR) case ENGINEID_TYPE_MACADDR: { int x; bufp[4] = ENGINEID_TYPE_MACADDR; /* * use default NIC if none provided */ if (NULL == engineIDNic) { x = getHwAddress(DEFAULT_NIC, &bufp[5]); } else { x = getHwAddress(engineIDNic, &bufp[5]); } if (0 != x) /* * function failed fill MAC address with zeros */ { memset(&bufp[5], 0, 6); } } break; #endif case ENGINEID_TYPE_IPV4: default: bufp[4] = ENGINEID_TYPE_IPV4; #ifdef HAVE_GETHOSTNAME if (hent && hent->h_addrtype == AF_INET) { memcpy(bufp + 5, hent->h_addr_list[0], hent->h_length); } else { /* Unknown address type. Default to 127.0.0.1. */ bufp[5] = 127; bufp[6] = 0; bufp[7] = 0; bufp[8] = 1; } #else /* HAVE_GETHOSTNAME */ /* * Unknown address type. Default to 127.0.0.1. */ bufp[5] = 127; bufp[6] = 0; bufp[7] = 0; bufp[8] = 1; #endif /* HAVE_GETHOSTNAME */ break; } /* * Pass the string back to the calling environment, or use it for * our local engineID. */ if (localsetup) { SNMP_FREE(engineID); engineID = bufp; engineIDLength = len; } else { *eidp = bufp; } return len; } /* end setup_engineID() */
main(int argc, char *argv[]) #endif { char options[128] = "aAc:CdD::fhHI:l:L:m:M:n:p:P:qrsS:UvV-:Y:"; int arg, i, ret; int dont_fork = 0, do_help = 0; int log_set = 0; int uid = 0, gid = 0; int agent_mode = -1; char *cptr, **argvptr; char *pid_file = NULL; char option_compatability[] = "-Le"; #if HAVE_GETPID int fd; FILE *PID; #endif #ifndef WIN32 /* * close all non-standard file descriptors we may have * inherited from the shell. */ for (i = getdtablesize() - 1; i > 2; --i) { (void) close(i); } #endif /* #WIN32 */ /* * register signals ASAP to prevent default action (usually core) * for signals during startup... */ #ifdef SIGTERM DEBUGMSGTL(("signal", "registering SIGTERM signal handler\n")); signal(SIGTERM, SnmpdShutDown); #endif #ifdef SIGINT DEBUGMSGTL(("signal", "registering SIGINT signal handler\n")); signal(SIGINT, SnmpdShutDown); #endif #ifdef SIGHUP signal(SIGHUP, SIG_IGN); /* do not terminate on early SIGHUP */ #endif #ifdef SIGUSR1 DEBUGMSGTL(("signal", "registering SIGUSR1 signal handler\n")); signal(SIGUSR1, SnmpdDump); #endif #ifdef SIGPIPE DEBUGMSGTL(("signal", "registering SIGPIPE signal handler\n")); signal(SIGPIPE, SIG_IGN); /* 'Inline' failure of wayward readers */ #endif #ifdef SIGXFSZ signal(SIGXFSZ, SnmpdCatchRandomSignal); #endif #ifdef NETSNMP_NO_ROOT_ACCESS /* * Default to no. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); #endif /* * Default to NOT running an AgentX master. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_MASTER, 0); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_TIMEOUT, -1); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_RETRIES, -1); netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_CACHE_TIMEOUT, 5); /* * Add some options if they are available. */ #if HAVE_UNISTD_H strcat(options, "g:u:"); #endif #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) strcat(options, "x:"); #endif #ifdef USING_AGENTX_SUBAGENT_MODULE strcat(options, "X"); #endif /* * This is incredibly ugly, but it's probably the simplest way * to handle the old '-L' option as well as the new '-Lx' style */ for (i=0; i<argc; i++) { if (!strcmp(argv[i], "-L")) argv[i] = option_compatability; } #ifdef WIN32 snmp_log_syslogname(app_name_long); #else snmp_log_syslogname(app_name); #endif netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE, app_name); /* * Now process options normally. */ while ((arg = getopt(argc, argv, options)) != EOF) { switch (arg) { case '-': if (strcasecmp(optarg, "help") == 0) { usage(argv[0]); } if (strcasecmp(optarg, "version") == 0) { version(); } handle_long_opt(optarg); break; case 'a': log_addresses++; break; case 'A': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES, 1); break; case 'c': if (optarg != NULL) { netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OPTIONALCONFIG, optarg); } else { usage(argv[0]); } break; case 'C': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_READ_CONFIGS, 1); break; case 'd': snmp_set_dump_packet(++snmp_dump_packet); netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE, 1); break; case 'D': debug_register_tokens(optarg); snmp_set_do_debugging(1); break; case 'f': dont_fork = 1; break; #if HAVE_UNISTD_H case 'g': if (optarg != NULL) { netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID, atoi(optarg)); } else { usage(argv[0]); } break; #endif case 'h': usage(argv[0]); break; case 'H': do_help = 1; break; case 'I': if (optarg != NULL) { add_to_init_list(optarg); } else { usage(argv[0]); } break; case 'l': printf("Warning: -l option is deprecated, use -Lf <file> instead\n"); if (optarg != NULL) { if (strlen(optarg) > PATH_MAX) { fprintf(stderr, "%s: logfile path too long (limit %d chars)\n", argv[0], PATH_MAX); exit(1); } snmp_enable_filelog(optarg, netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES)); log_set = 1; } else { usage(argv[0]); } break; case 'L': if (snmp_log_options( optarg, argc, argv ) < 0 ) { usage(argv[0]); } log_set = 1; break; case 'm': if (optarg != NULL) { setenv("MIBS", optarg, 1); } else { usage(argv[0]); } break; case 'M': if (optarg != NULL) { setenv("MIBDIRS", optarg, 1); } else { usage(argv[0]); } break; case 'n': if (optarg != NULL) { app_name = optarg; netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPTYPE, app_name); } else { usage(argv[0]); } break; case 'P': printf("Warning: -P option is deprecated, use -p instead\n"); case 'p': if (optarg != NULL) { pid_file = optarg; } else { usage(argv[0]); } break; case 'q': snmp_set_quick_print(1); break; case 'r': netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS); break; case 's': printf("Warning: -s option is deprecated, use -Lsd instead\n"); snmp_enable_syslog(); log_set = 1; break; case 'S': printf("Warning: -S option is deprecated, use -Ls <facility> instead\n"); if (optarg != NULL) { switch (*optarg) { case 'd': case 'D': Facility = LOG_DAEMON; break; case 'i': case 'I': Facility = LOG_INFO; break; case '0': Facility = LOG_LOCAL0; break; case '1': Facility = LOG_LOCAL1; break; case '2': Facility = LOG_LOCAL2; break; case '3': Facility = LOG_LOCAL3; break; case '4': Facility = LOG_LOCAL4; break; case '5': Facility = LOG_LOCAL5; break; case '6': Facility = LOG_LOCAL6; break; case '7': Facility = LOG_LOCAL7; break; default: fprintf(stderr, "invalid syslog facility: -S%c\n",*optarg); usage(argv[0]); } snmp_enable_syslog_ident(snmp_log_syslogname(NULL), Facility); log_set = 1; } else { fprintf(stderr, "no syslog facility specified\n"); usage(argv[0]); } break; case 'U': netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_LEAVE_PIDFILE); break; #if HAVE_UNISTD_H case 'u': if (optarg != NULL) { char *ecp; int uid; uid = strtoul(optarg, &ecp, 10); if (*ecp) { #if HAVE_GETPWNAM && HAVE_PWD_H struct passwd *info; info = getpwnam(optarg); if (info) { uid = info->pw_uid; } else { #endif fprintf(stderr, "Bad user id: %s\n", optarg); exit(1); #if HAVE_GETPWNAM && HAVE_PWD_H } #endif } netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID, uid); } else { usage(argv[0]); } break; #endif case 'v': version(); case 'V': netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_VERBOSE, 1); break; #if defined(USING_AGENTX_SUBAGENT_MODULE)|| defined(USING_AGENTX_MASTER_MODULE) case 'x': if (optarg != NULL) { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, optarg); } else { usage(argv[0]); } netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_AGENTX_MASTER, 1); break; #endif case 'X': #if defined(USING_AGENTX_SUBAGENT_MODULE) agent_mode = SUB_AGENT; #else fprintf(stderr, "%s: Illegal argument -X:" "AgentX support not compiled in.\n", argv[0]); usage(argv[0]); exit(1); #endif break; case 'Y': netsnmp_config_remember(optarg); break; default: usage(argv[0]); break; } } if (do_help) { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS, 1); init_agent(app_name); /* register our .conf handlers */ init_mib_modules(); init_snmp(app_name); fprintf(stderr, "Configuration directives understood:\n"); read_config_print_usage(" "); exit(0); } if (optind < argc) { /* * There are optional transport addresses on the command line. */ DEBUGMSGTL(("snmpd/main", "optind %d, argc %d\n", optind, argc)); for (i = optind; i < argc; i++) { char *c, *astring; if ((c = netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS))) { astring = malloc(strlen(c) + 2 + strlen(argv[i])); if (astring == NULL) { fprintf(stderr, "malloc failure processing argv[%d]\n", i); exit(1); } sprintf(astring, "%s,%s", c, argv[i]); netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS, astring); SNMP_FREE(astring); } else { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS, argv[i]); } } DEBUGMSGTL(("snmpd/main", "port spec: %s\n", netsnmp_ds_get_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PORTS))); } #ifdef NETSNMP_LOGFILE if (0 == log_set) snmp_enable_filelog(NETSNMP_LOGFILE, netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES)); #endif /* * Initialize a argv set to the current for restarting the agent. */ argvrestartp = (char **)malloc((argc + 2) * sizeof(char *)); argvptr = argvrestartp; for (i = 0, ret = 1; i < argc; i++) { ret += strlen(argv[i]) + 1; } argvrestart = (char *) malloc(ret); argvrestartname = (char *) malloc(strlen(argv[0]) + 1); if (!argvrestartp || !argvrestart || !argvrestartname) { fprintf(stderr, "malloc failure processing argvrestart\n"); exit(1); } strcpy(argvrestartname, argv[0]); if (agent_mode == -1) { if (strstr(argvrestartname, "agentxd") != NULL) { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, SUB_AGENT); } else { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, MASTER_AGENT); } } else { netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, agent_mode); } for (cptr = argvrestart, i = 0; i < argc; i++) { strcpy(cptr, argv[i]); *(argvptr++) = cptr; cptr += strlen(argv[i]) + 1; } *cptr = 0; *argvptr = NULL; #ifdef BUFSIZ setvbuf(stdout, NULL, _IOLBF, BUFSIZ); #endif /* * Initialize the world. Detach from the shell. Create initial user. */ if(!dont_fork) { int quit = ! netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_QUIT_IMMEDIATELY); ret = netsnmp_daemonize(quit, snmp_stderrlog_status()); /* * xxx-rks: do we care if fork fails? I think we should... */ if(ret != 0) Exit(1); /* Exit logs exit val for us */ } SOCK_STARTUP; init_agent(app_name); /* do what we need to do first. */ init_mib_modules(); /* * start library */ init_snmp(app_name); if ((ret = init_master_agent()) != 0) { /* * Some error opening one of the specified agent transports. */ Exit(1); /* Exit logs exit val for us */ } #if HAVE_GETPID if (pid_file != NULL) { /* * unlink the pid_file, if it exists, prior to open. Without * doing this the open will fail if the user specified pid_file * already exists. */ unlink(pid_file); fd = open(pid_file, O_CREAT | O_EXCL | O_WRONLY, 0600); if (fd == -1) { snmp_log_perror(pid_file); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } else { if ((PID = fdopen(fd, "w")) == NULL) { snmp_log_perror(pid_file); exit(1); } else { fprintf(PID, "%d\n", (int) getpid()); fclose(PID); } close(fd); } } #endif #if HAVE_UNISTD_H cptr = get_persistent_directory(); mkdirhier( cptr, NETSNMP_AGENT_DIRECTORY_MODE, 0 ); uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID); gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID); #ifdef HAVE_CHOWN if ( uid != 0 || gid != 0 ) chown( cptr, uid, gid ); #endif #ifdef HAVE_SETGID if ((gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID)) != 0) { DEBUGMSGTL(("snmpd/main", "Changing gid to %d.\n", gid)); if (setgid(gid) == -1 #ifdef HAVE_SETGROUPS || setgroups(1, (gid_t *)&gid) == -1 #endif ) { snmp_log_perror("setgid failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif #ifdef HAVE_SETUID if ((uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID)) != 0) { DEBUGMSGTL(("snmpd/main", "Changing uid to %d.\n", uid)); if (setuid(uid) == -1) { snmp_log_perror("setuid failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif #endif /* * Store persistent data immediately in case we crash later. */ snmp_store(app_name); #ifdef SIGHUP DEBUGMSGTL(("signal", "registering SIGHUP signal handler\n")); signal(SIGHUP, SnmpdReconfig); #endif /* * Send coldstart trap if possible. */ send_easy_trap(0, 0); /* * We're up, log our version number. */ snmp_log(LOG_INFO, "NET-SNMP version %s\n", netsnmp_get_version()); #ifdef WIN32SERVICE agent_status = AGENT_RUNNING; #endif netsnmp_addrcache_initialise(); /* * Forever monitor the dest_port for incoming PDUs. */ DEBUGMSGTL(("snmpd/main", "We're up. Starting to process data.\n")); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_QUIT_IMMEDIATELY)) receive(); DEBUGMSGTL(("snmpd/main", "sending shutdown trap\n")); SnmpTrapNodeDown(); DEBUGMSGTL(("snmpd/main", "Bye...\n")); snmp_shutdown(app_name); #ifdef SHUTDOWN_AGENT_CLEANLY /* broken code */ /* these attempt to free all known memory, but result in double frees */ shutdown_master_agent(); shutdown_agent(); #endif if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_LEAVE_PIDFILE) && (pid_file != NULL)) { unlink(pid_file); } #ifdef WIN32SERVICE agent_status = AGENT_STOPPED; #endif SNMP_FREE(argvrestartname); SNMP_FREE(argvrestart); SNMP_FREE(argvrestartp); SOCK_CLEANUP; return 0; } /* End main() -- snmpd */
void usm_parse_create_usmUser(const char *token, char *line) { char *cp; char buf[SNMP_MAXBUF_MEDIUM]; struct usmUser *newuser; u_char userKey[SNMP_MAXBUF_SMALL], *tmpp; size_t userKeyLen = SNMP_MAXBUF_SMALL; size_t privKeyLen = 0; size_t ret; int testcase; newuser = usm_create_user(); /* * READ: Security Name */ cp = copy_nword(line, buf, sizeof(buf)); /* * might be a -e ENGINEID argument */ if (strcmp(buf, "-e") == 0) { size_t ebuf_len = 32, eout_len = 0; u_char *ebuf = (u_char *) malloc(ebuf_len); if (ebuf == NULL) { config_perror("malloc failure processing -e flag"); usm_free_user(newuser); return; } /* * Get the specified engineid from the line. */ cp = copy_nword(cp, buf, sizeof(buf)); if (!snmp_hex_to_binary(&ebuf, &ebuf_len, &eout_len, 1, buf)) { config_perror("invalid EngineID argument to -e"); usm_free_user(newuser); SNMP_FREE(ebuf); return; } newuser->engineID = ebuf; newuser->engineIDLen = eout_len; cp = copy_nword(cp, buf, sizeof(buf)); } else { newuser->engineID = snmpv3_generate_engineID(&ret); if (ret == 0) { usm_free_user(newuser); return; } newuser->engineIDLen = ret; } newuser->secName = strdup(buf); newuser->name = strdup(buf); if (!cp) goto add; /* no authentication or privacy type */ /* * READ: Authentication Type */ #ifndef DISABLE_MD5 if (strncmp(cp, "MD5", 3) == 0) { memcpy(newuser->authProtocol, usmHMACMD5AuthProtocol, sizeof(usmHMACMD5AuthProtocol)); } else #endif if (strncmp(cp, "SHA", 3) == 0) { memcpy(newuser->authProtocol, usmHMACSHA1AuthProtocol, sizeof(usmHMACSHA1AuthProtocol)); } else { config_perror("Unknown authentication protocol"); usm_free_user(newuser); return; } cp = skip_token(cp); /* * READ: Authentication Pass Phrase or key */ if (!cp) { config_perror("no authentication pass phrase"); usm_free_user(newuser); return; } cp = copy_nword(cp, buf, sizeof(buf)); if (strcmp(buf,"-m") == 0) { /* a master key is specified */ cp = copy_nword(cp, buf, sizeof(buf)); ret = sizeof(userKey); tmpp = userKey; userKeyLen = 0; if (!snmp_hex_to_binary(&tmpp, &ret, &userKeyLen, 0, buf)) { config_perror("invalid key value argument to -m"); usm_free_user(newuser); return; } } else if (strcmp(buf,"-l") != 0) { /* a password is specified */ userKeyLen = sizeof(userKey); ret = generate_Ku(newuser->authProtocol, newuser->authProtocolLen, (u_char *) buf, strlen(buf), userKey, &userKeyLen); if (ret != SNMPERR_SUCCESS) { config_perror("could not generate the authentication key from the " "suppiled pass phrase."); usm_free_user(newuser); return; } } /* * And turn it into a localized key */ ret = sc_get_properlength(newuser->authProtocol, newuser->authProtocolLen); if (ret <= 0) { config_perror("Could not get proper authentication protocol key length"); return; } newuser->authKey = (u_char *) malloc(ret); if (strcmp(buf,"-l") == 0) { /* a local key is directly specified */ cp = copy_nword(cp, buf, sizeof(buf)); newuser->authKeyLen = 0; if (!snmp_hex_to_binary(&newuser->authKey, &ret, &newuser->authKeyLen, 0, buf)) { config_perror("invalid key value argument to -l"); usm_free_user(newuser); return; } if (ret != newuser->authKeyLen) { config_perror("improper key length to -l"); usm_free_user(newuser); return; } } else { newuser->authKeyLen = ret; ret = generate_kul(newuser->authProtocol, newuser->authProtocolLen, newuser->engineID, newuser->engineIDLen, userKey, userKeyLen, newuser->authKey, &newuser->authKeyLen); if (ret != SNMPERR_SUCCESS) { config_perror("could not generate localized authentication key " "(Kul) from the master key (Ku)."); usm_free_user(newuser); return; } } if (!cp) goto add; /* no privacy type (which is legal) */ /* * READ: Privacy Type */ testcase = 0; #ifndef DISABLE_DES if (strncmp(cp, "DES", 3) == 0) { memcpy(newuser->privProtocol, usmDESPrivProtocol, sizeof(usmDESPrivProtocol)); testcase = 1; /* DES uses a 128 bit key, 64 bits of which is a salt */ privKeyLen = 16; } #endif #ifdef HAVE_AES if (strncmp(cp, "AES128", 6) == 0 || strncmp(cp, "AES", 3) == 0) { memcpy(newuser->privProtocol, usmAESPrivProtocol, sizeof(usmAESPrivProtocol)); testcase = 1; privKeyLen = 16; } #endif if (testcase == 0) { config_perror("Unknown privacy protocol"); usm_free_user(newuser); return; } cp = skip_token(cp); /* * READ: Encryption Pass Phrase or key */ if (!cp) { /* * assume the same as the authentication key */ memdup(&newuser->privKey, newuser->authKey, newuser->authKeyLen); newuser->privKeyLen = newuser->authKeyLen; } else { cp = copy_nword(cp, buf, sizeof(buf)); if (strcmp(buf,"-m") == 0) { /* a master key is specified */ cp = copy_nword(cp, buf, sizeof(buf)); ret = sizeof(userKey); tmpp = userKey; userKeyLen = 0; if (!snmp_hex_to_binary(&tmpp, &ret, &userKeyLen, 0, buf)) { config_perror("invalid key value argument to -m"); usm_free_user(newuser); return; } } else if (strcmp(buf,"-l") != 0) { /* a password is specified */ userKeyLen = sizeof(userKey); ret = generate_Ku(newuser->authProtocol, newuser->authProtocolLen, (u_char *) buf, strlen(buf), userKey, &userKeyLen); if (ret != SNMPERR_SUCCESS) { config_perror("could not generate the privacy key from the " "suppiled pass phrase."); usm_free_user(newuser); return; } } /* * And turn it into a localized key */ ret = sc_get_properlength(newuser->authProtocol, newuser->authProtocolLen); if (ret < 0) { config_perror("could not get proper key length to use for the " "privacy algorithm."); usm_free_user(newuser); return; } newuser->privKey = (u_char *) malloc(ret); if (strcmp(buf,"-l") == 0) { /* a local key is directly specified */ cp = copy_nword(cp, buf, sizeof(buf)); newuser->privKeyLen = 0; if (!snmp_hex_to_binary(&newuser->privKey, &ret, &newuser->privKeyLen, 0, buf)) { config_perror("invalid key value argument to -l"); usm_free_user(newuser); return; } } else { newuser->privKeyLen = ret; ret = generate_kul(newuser->authProtocol, newuser->authProtocolLen, newuser->engineID, newuser->engineIDLen, userKey, userKeyLen, newuser->privKey, &newuser->privKeyLen); if (ret != SNMPERR_SUCCESS) { config_perror("could not generate localized privacy key " "(Kul) from the master key (Ku)."); usm_free_user(newuser); return; } } } if ((newuser->privKeyLen >= privKeyLen) || (privKeyLen == 0)){ newuser->privKeyLen = privKeyLen; } else { /* The privKey length is smaller than required by privProtocol */ usm_free_user(newuser); return; } add: usm_add_user(newuser); DEBUGMSGTL(("usmUser", "created a new user %s at ", newuser->secName)); DEBUGMSGHEX(("usmUser", newuser->engineID, newuser->engineIDLen)); DEBUGMSG(("usmUser", "\n")); }
/** @internal */ void netsnmp_config_parse_add_row(const char *token, char *line) { char buf[SNMP_MAXBUF_MEDIUM]; char tname[SNMP_MAXBUF_MEDIUM]; size_t buf_size; int rc; data_set_tables *tables; netsnmp_variable_list *vb; /* containing only types */ netsnmp_table_row *row; netsnmp_table_data_set_storage *dr; line = copy_nword(line, tname, SNMP_MAXBUF_MEDIUM); tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, tname); if (!tables) { config_pwarn("Unknown table trying to add a row"); return; } /* * do the indexes first */ row = netsnmp_create_table_data_row(); for (vb = tables->table_set->table->indexes_template; vb; vb = vb->next_variable) { if (!line) { config_pwarn("missing an index value"); SNMP_FREE (row); return; } DEBUGMSGTL(("table_set_add_row", "adding index of type %d\n", vb->type)); buf_size = SNMP_MAXBUF_MEDIUM; line = read_config_read_memory(vb->type, line, buf, &buf_size); netsnmp_table_row_add_index(row, vb->type, buf, buf_size); } /* * then do the data */ for (dr = tables->table_set->default_row; dr; dr = dr->next) { if (!line) { config_pwarn("missing a data value. " "All columns must be specified."); snmp_log(LOG_WARNING," can't find value for column %d\n", dr->column - 1); SNMP_FREE (row); return; } buf_size = SNMP_MAXBUF_MEDIUM; line = read_config_read_memory(dr->type, line, buf, &buf_size); DEBUGMSGTL(("table_set_add_row", "adding data at column %d of type %d\n", dr->column, dr->type)); netsnmp_set_row_column(row, dr->column, dr->type, buf, buf_size); if (dr->writable) netsnmp_mark_row_column_writable(row, dr->column, 1); /* make writable */ } rc = netsnmp_table_data_add_row(tables->table_set->table, row); if (SNMPERR_SUCCESS != rc) { config_pwarn("error adding table row"); } if (NULL != line) { config_pwarn("extra data value. Too many columns specified."); snmp_log(LOG_WARNING," extra data '%s'\n", line); } }
/*******************************************************************-o-****** * setup_engineID * * Parameters: * **eidp * *text Printable (?) text to be plugged into the snmpEngineID. * * Return: * Length of allocated engineID string in bytes, -OR- * -1 on error. * * * Create an snmpEngineID using text and the local IP address. If eidp * is defined, use it to return a pointer to the newly allocated data. * Otherwise, use the result to define engineID defined in this module. * * Line syntax: * engineID <text> | NULL * * XXX What if a node has multiple interfaces? * XXX What if multiple engines all choose the same address? * (answer: You're screwed, because you might need a kul database * which is dependant on the current engineID. Enumeration and other * tricks won't work). */ int setup_engineID(u_char **eidp, const char *text) { int enterpriseid = htonl(ENTERPRISE_NUMBER), localsetup = (eidp) ? 0 : 1; /* Use local engineID if *eidp == NULL. */ #ifdef HAVE_GETHOSTNAME u_char buf[SNMP_MAXBUF_SMALL]; struct hostent *hent; #endif u_char *bufp = NULL; size_t len; /* * Determine length of the engineID string. */ if (text) { len = 5+strlen(text); /* 5 leading bytes+text. */ } else { len = 5 + 4; /* 5 leading bytes + four byte IPv4 address */ #ifdef HAVE_GETHOSTNAME gethostname((char *)buf, sizeof(buf)); hent = gethostbyname((char *)buf); #ifdef AF_INET6 if (hent && hent->h_addrtype == AF_INET6) len += 12; /* 16 bytes total for IPv6 address. */ #endif #endif /* HAVE_GETHOSTNAME */ } /* endif -- text (1) */ /* * Allocate memory and store enterprise ID. */ if ((bufp = (u_char *) malloc(len)) == NULL) { snmp_log_perror("setup_engineID malloc"); return -1; } memcpy(bufp, &enterpriseid, sizeof(enterpriseid)); /* XXX Must be 4 bytes! */ bufp[0] |= 0x80; /* * Store the given text -OR- the first found IP address. */ if (text) { bufp[4] = 4; memcpy((char *)bufp+5, text, strlen(text)); } else { bufp[4] = 1; #ifdef HAVE_GETHOSTNAME gethostname((char *)buf, sizeof(buf)); hent = gethostbyname((char *)buf); if (hent && hent->h_addrtype == AF_INET) { memcpy(bufp+5, hent->h_addr_list[0], hent->h_length); #ifdef AF_INET6 } else if (hent && hent->h_addrtype == AF_INET6) { bufp[4] = 2; memcpy(bufp+5, hent->h_addr_list[0], hent->h_length); #endif } else { /* Unknown address type. Default to 127.0.0.1. */ bufp[5] = 127; bufp[6] = 0; bufp[7] = 0; bufp[8] = 1; } #else /* HAVE_GETHOSTNAME */ /* Unknown address type. Default to 127.0.0.1. */ bufp[5] = 127; bufp[6] = 0; bufp[7] = 0; bufp[8] = 1; #endif /* HAVE_GETHOSTNAME */ } /* endif -- text (2) */ /* * Pass the string back to the calling environment, or use it for * our local engineID. */ if (localsetup) { SNMP_FREE(engineID); engineID = bufp; engineIDLength = len; } else { *eidp = bufp; } return len; } /* end setup_engineID() */
/* implements the table data helper. This is the routine that takes * care of all SNMP requests coming into the table. */ int netsnmp_table_data_set_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_table_data_set_storage *data = NULL; newrow_stash *newrowstash = NULL; netsnmp_table_row *row, *newrow = NULL; netsnmp_table_request_info *table_info; netsnmp_request_info *request; netsnmp_oid_stash_node **stashp = NULL; if (!handler) return SNMPERR_GENERR; DEBUGMSGTL(("netsnmp_table_data_set", "handler starting\n")); for (request = requests; request; request = request->next) { netsnmp_table_data_set *datatable = (netsnmp_table_data_set *) handler->myvoid; const oid * const suffix = requests->requestvb->name + reginfo->rootoid_len + 2; const size_t suffix_len = requests->requestvb->name_length - (reginfo->rootoid_len + 2); if (request->processed) continue; /* * extract our stored data and table info */ row = netsnmp_extract_table_row(request); table_info = netsnmp_extract_table_info(request); if (MODE_IS_SET(reqinfo->mode)) { char buf[256]; /* is this reasonable size?? */ int rc; size_t len; /* * use a cached copy of the row for modification */ /* * cache location: may have been created already by other * SET requests in the same master request. */ rc = snprintf(buf, sizeof(buf), "dataset_row_stash:%s:", datatable->table->name); if ((-1 == rc) || (rc >= sizeof(buf))) { snmp_log(LOG_ERR,"%s handler name too long\n", datatable->table->name); netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; } len = sizeof(buf) - rc; rc = snprint_objid(&buf[rc], len, table_info->index_oid, table_info->index_oid_len); if (-1 == rc) { snmp_log(LOG_ERR,"%s oid or name too long\n", datatable->table->name); netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; } stashp = (netsnmp_oid_stash_node **) netsnmp_table_get_or_create_row_stash(reqinfo, (u_char*)buf); newrowstash = netsnmp_oid_stash_get_data(*stashp, suffix, suffix_len); if (!newrowstash) { if (!row) { if (datatable->allow_creation) { /* * entirely new row. Create the row from the template */ newrowstash = netsnmp_table_data_set_create_newrowstash( datatable, table_info); newrow = newrowstash->newrow; } else if (datatable->rowstatus_column == 0) { /* * A RowStatus object may be used to control the * creation of a new row. But if this object * isn't declared (and the table isn't marked as * 'auto-create'), then we can't create a new row. */ netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOCREATION); continue; } } else { /* * existing row that needs to be modified */ newrowstash = SNMP_MALLOC_TYPEDEF(newrow_stash); if (newrowstash == NULL) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_GENERR); continue; } newrow = netsnmp_table_data_set_clone_row(row); newrowstash->newrow = newrow; } netsnmp_oid_stash_add_data(stashp, suffix, suffix_len, newrowstash); } else { newrow = newrowstash->newrow; } /* * all future SET data modification operations use this * temp pointer */ if (reqinfo->mode == MODE_SET_RESERVE1 || reqinfo->mode == MODE_SET_RESERVE2) row = newrow; } if (row) data = (netsnmp_table_data_set_storage *) row->data; if (!row || !table_info || !data) { if (!MODE_IS_SET(reqinfo->mode) || !table_info) { netsnmp_set_request_error(reqinfo, request, SNMP_NOSUCHINSTANCE); continue; } } data = netsnmp_table_data_set_find_column(data, table_info->colnum); switch (reqinfo->mode) { case MODE_GET: case MODE_GETNEXT: case MODE_GETBULK: /* XXXWWW */ if (data && data->data.voidp) netsnmp_table_data_build_result(reginfo, reqinfo, request, row, table_info->colnum, data->type, data->data.voidp, data->data_len); break; case MODE_SET_RESERVE1: if (data) { /* * Can we modify the existing row? */ if (!data->writable) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOTWRITABLE); } else if (request->requestvb->type != data->type) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGTYPE); } } else if (datatable->rowstatus_column == table_info->colnum) { /* * Otherwise, this is where we create a new row using * the RowStatus object (essentially duplicating the * steps followed earlier in the 'allow_creation' case) */ switch (*(request->requestvb->val.integer)) { case RS_CREATEANDGO: case RS_CREATEANDWAIT: newrowstash = netsnmp_table_data_set_create_newrowstash( datatable, table_info); newrow = newrowstash->newrow; row = newrow; netsnmp_oid_stash_add_data(stashp, suffix, suffix_len, newrowstash); } } break; case MODE_SET_RESERVE2: /* * If the agent receives a SET request for an object in a non-existant * row, then the RESERVE1 pass will create the row automatically. * * But since the row doesn't exist at that point, the test for whether * the object is writable or not will be skipped. So we need to check * for this possibility again here. * * Similarly, if row creation is under the control of the RowStatus * object (i.e. allow_creation == 0), but this particular request * doesn't include such an object, then the row won't have been created, * and the writable check will also have been skipped. Again - check here. */ if (data && data->writable == 0) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOTWRITABLE); continue; } if (datatable->rowstatus_column == table_info->colnum) { switch (*(request->requestvb->val.integer)) { case RS_ACTIVE: case RS_NOTINSERVICE: /* * Can only operate on pre-existing rows. */ if (!newrowstash || newrowstash->created) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_INCONSISTENTVALUE); continue; } break; case RS_CREATEANDGO: case RS_CREATEANDWAIT: /* * Can only operate on newly created rows. */ if (!(newrowstash && newrowstash->created)) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_INCONSISTENTVALUE); continue; } break; case RS_DESTROY: /* * Can operate on new or pre-existing rows. */ break; case RS_NOTREADY: default: /* * Not a valid value to Set */ netsnmp_set_request_error(reqinfo, request, SNMP_ERR_WRONGVALUE); continue; } } if (!data ) { netsnmp_set_request_error(reqinfo, request, SNMP_ERR_NOCREATION); continue; } /* * modify row and set new value */ SNMP_FREE(data->data.string); data->data.string = (u_char*)netsnmp_strdup_and_null(request->requestvb->val.string, request->requestvb->val_len); if (!data->data.string) { netsnmp_set_request_error(reqinfo, requests, SNMP_ERR_RESOURCEUNAVAILABLE); } data->data_len = request->requestvb->val_len; if (datatable->rowstatus_column == table_info->colnum) { switch (*(request->requestvb->val.integer)) { case RS_CREATEANDGO: /* * XXX: check legality */ *(data->data.integer) = RS_ACTIVE; break; case RS_CREATEANDWAIT: /* * XXX: check legality */ *(data->data.integer) = RS_NOTINSERVICE; break; case RS_DESTROY: newrowstash->deleted = 1; break; } } break; case MODE_SET_ACTION: /* * Install the new row into the stored table. * Do this only *once* per row .... */ if (newrowstash->state != STATE_ACTION) { newrowstash->state = STATE_ACTION; if (newrowstash->created) { netsnmp_table_dataset_add_row(datatable, newrow); } else { netsnmp_table_dataset_replace_row(datatable, row, newrow); } } /* * ... but every (relevant) varbind in the request will * need to know about this new row, so update the * per-request row information regardless */ if (newrowstash->created) { netsnmp_request_add_list_data(request, netsnmp_create_data_list(TABLE_DATA_NAME, newrow, NULL)); } break; case MODE_SET_UNDO: /* * extract the new row, replace with the old or delete */ if (newrowstash->state != STATE_UNDO) { newrowstash->state = STATE_UNDO; if (newrowstash->created) { netsnmp_table_dataset_remove_and_delete_row(datatable, newrow); } else { netsnmp_table_dataset_replace_row(datatable, newrow, row); netsnmp_table_dataset_delete_row(newrow); } newrow = NULL; } break; case MODE_SET_COMMIT: if (newrowstash->state != STATE_COMMIT) { newrowstash->state = STATE_COMMIT; if (!newrowstash->created) { netsnmp_request_info *req; netsnmp_table_dataset_delete_row(row); /* Walk the request list to update the reference to the old row w/ th new one */ for (req = requests; req; req=req->next) { /* * For requests that have the old row values, * so add the newly-created row information. */ if ((netsnmp_table_row *) netsnmp_extract_table_row(req) == row) { netsnmp_request_remove_list_data(req, TABLE_DATA_ROW); netsnmp_request_add_list_data(req, netsnmp_create_data_list(TABLE_DATA_ROW, newrow, NULL)); } } row = NULL; } if (newrowstash->deleted) { netsnmp_table_dataset_remove_and_delete_row(datatable, newrow); newrow = NULL; } } break; case MODE_SET_FREE: if (newrowstash && newrowstash->state != STATE_FREE) { newrowstash->state = STATE_FREE; netsnmp_table_dataset_delete_row(newrow); newrow = NULL; } break; } } /* next handler called automatically - 'AUTO_NEXT' */ return SNMP_ERR_NOERROR; }
int get_USM_DH_key(netsnmp_variable_list *vars, netsnmp_variable_list *dhvar, size_t outkey_len, netsnmp_pdu *pdu, const char *keyname, oid *keyoid, size_t keyoid_len) { u_char *dhkeychange; DH *dh; BIGNUM *other_pub; u_char *key; size_t key_len; unsigned char *cp; dhkeychange = (u_char *) malloc(2 * vars->val_len * sizeof(char)); if (!dhkeychange) return SNMPERR_GENERR; memcpy(dhkeychange, vars->val.string, vars->val_len); cp = dhvar->val.string; dh = d2i_DHparams(NULL, (const unsigned char **) &cp, dhvar->val_len); if (!dh || !dh->g || !dh->p) { SNMP_FREE(dhkeychange); return SNMPERR_GENERR; } DH_generate_key(dh); if (!dh->pub_key) { SNMP_FREE(dhkeychange); return SNMPERR_GENERR; } if (vars->val_len != BN_num_bytes(dh->pub_key)) { SNMP_FREE(dhkeychange); fprintf(stderr,"incorrect diffie-helman lengths (%lu != %d)\n", (unsigned long)vars->val_len, BN_num_bytes(dh->pub_key)); return SNMPERR_GENERR; } BN_bn2bin(dh->pub_key, dhkeychange + vars->val_len); key_len = DH_size(dh); if (!key_len) { SNMP_FREE(dhkeychange); return SNMPERR_GENERR; } key = (u_char *) malloc(key_len * sizeof(u_char)); if (!key) { SNMP_FREE(dhkeychange); return SNMPERR_GENERR; } other_pub = BN_bin2bn(vars->val.string, vars->val_len, NULL); if (!other_pub) { SNMP_FREE(dhkeychange); SNMP_FREE(key); return SNMPERR_GENERR; } if (DH_compute_key(key, other_pub, dh)) { u_char *kp; printf("new %s key: 0x", keyname); for(kp = key + key_len - outkey_len; kp - key < key_len; kp++) { printf("%02x", (unsigned char) *kp); } printf("\n"); } snmp_pdu_add_variable(pdu, keyoid, keyoid_len, ASN_OCTET_STR, dhkeychange, 2 * vars->val_len); SNMP_FREE(dhkeychange); SNMP_FREE(other_pub); SNMP_FREE(key); return SNMPERR_SUCCESS; }
/** @internal */ void netsnmp_config_parse_table_set(const char *token, char *line) { oid table_name[MAX_OID_LEN]; size_t table_name_length = MAX_OID_LEN; struct tree *tp; netsnmp_table_data_set *table_set; data_set_tables *tables; unsigned int mincol = 0xffffff, maxcol = 0; char *pos; /* * instatiate a fake table based on MIB information */ DEBUGMSGTL(("9:table_set_add_table", "processing '%s'\n", line)); if (NULL != (pos = strchr(line,' '))) { config_pwarn("ignoring extra tokens on line"); snmp_log(LOG_WARNING," ignoring '%s'\n", pos); *pos = '\0'; } /* * check for duplicate table */ tables = (data_set_tables *) netsnmp_get_list_data(auto_tables, line); if (NULL != tables) { config_pwarn("duplicate table definition"); return; } /* * parse oid and find tree structure */ if (!snmp_parse_oid(line, table_name, &table_name_length)) { config_pwarn ("can't instatiate table since I can't parse the table name"); return; } if(NULL == (tp = get_tree(table_name, table_name_length, get_tree_head()))) { config_pwarn("can't instatiate table since " "I can't find mib information about it"); return; } if (NULL == (tp = tp->child_list) || NULL == tp->child_list) { config_pwarn("can't instatiate table since it doesn't appear to be " "a proper table (no children)"); return; } table_set = netsnmp_create_table_data_set(line); /* * check for augments indexes */ if (NULL != tp->augments) { oid name[MAX_OID_LEN]; size_t name_length = MAX_OID_LEN; struct tree *tp2; if (!snmp_parse_oid(tp->augments, name, &name_length)) { config_pwarn("I can't parse the augment tabel name"); snmp_log(LOG_WARNING, " can't parse %s\n", tp->augments); SNMP_FREE (table_set); return; } if(NULL == (tp2 = get_tree(name, name_length, get_tree_head()))) { config_pwarn("can't instatiate table since " "I can't find mib information about augment table"); snmp_log(LOG_WARNING, " table %s not found in tree\n", tp->augments); SNMP_FREE (table_set); return; } _table_set_add_indexes(table_set, tp2); } _table_set_add_indexes(table_set, tp); /* * loop through children and add each column info */ for (tp = tp->child_list; tp; tp = tp->next_peer) { int canwrite = 0; u_char type; type = mib_to_asn_type(tp->type); if (type == (u_char) - 1) { config_pwarn("unknown column type"); SNMP_FREE (table_set); return; /* xxx mem leak */ } DEBUGMSGTL(("table_set_add_table", "adding column %s(%ld) of type %d (access %d)\n", tp->label, tp->subid, type, tp->access)); switch (tp->access) { case MIB_ACCESS_CREATE: table_set->allow_creation = 1; case MIB_ACCESS_READWRITE: case MIB_ACCESS_WRITEONLY: canwrite = 1; case MIB_ACCESS_READONLY: DEBUGMSGTL(("table_set_add_table", "adding column %ld of type %d\n", tp->subid, type)); netsnmp_table_set_add_default_row(table_set, tp->subid, type, canwrite, NULL, 0); mincol = SNMP_MIN(mincol, tp->subid); maxcol = SNMP_MAX(maxcol, tp->subid); break; case MIB_ACCESS_NOACCESS: case MIB_ACCESS_NOTIFY: break; default: config_pwarn("unknown column access type"); break; } } /* * register the table */ netsnmp_register_table_data_set(netsnmp_create_handler_registration (line, NULL, table_name, table_name_length, HANDLER_CAN_RWRITE), table_set, NULL); netsnmp_register_auto_data_table(table_set, NULL); }
/*******************************************************************-o-****** * set_enginetime * * Parameters: * *engineID * engineID_len * engineboot * engine_time * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_GENERR Otherwise. * * * Lookup engineID and store the given <engine_time, engineboot> tuple * and then stamp the record with a consistent source of local time. * If the engineID record does not exist, create one. * * Special case: engineID is NULL or engineID_len is 0 defines an engineID * that is "always set." * * XXX "Current time within the local engine" == time(NULL)... */ int set_enginetime(u_char * engineID, u_int engineID_len, u_int engineboot, u_int engine_time, u_int authenticated) { int rval = SNMPERR_SUCCESS, iindex; Enginetime e = NULL; /* * Sanity check. */ if (!engineID || (engineID_len <= 0)) { return rval; } /* * Store the given <engine_time, engineboot> tuple in the record * for engineID. Create a new record if necessary. */ if (!(e = search_enginetime_list(engineID, engineID_len))) { if ((iindex = hash_engineID(engineID, engineID_len)) < 0) { QUITFUN(SNMPERR_GENERR, set_enginetime_quit); } e = (Enginetime) calloc(1, sizeof(*e)); e->next = etimelist[iindex]; etimelist[iindex] = e; e->engineID = (u_char *) calloc(1, engineID_len); memcpy(e->engineID, engineID, engineID_len); e->engineID_len = engineID_len; } #ifdef LCD_TIME_SYNC_OPT if (authenticated || !e->authenticatedFlag) { e->authenticatedFlag = authenticated; #else if (authenticated) { #endif e->engineTime = engine_time; e->engineBoot = engineboot; e->lastReceivedEngineTime = snmpv3_local_snmpEngineTime(); } e = NULL; /* Indicates a successful update. */ DEBUGMSGTL(("lcd_set_enginetime", "engineID ")); DEBUGMSGHEX(("lcd_set_enginetime", engineID, engineID_len)); DEBUGMSG(("lcd_set_enginetime", ": boots=%d, time=%d\n", engineboot, engine_time)); set_enginetime_quit: SNMP_FREE(e); return rval; } /* end set_enginetime() */ /*******************************************************************-o-****** * search_enginetime_list * * Parameters: * *engineID * engineID_len * * Returns: * Pointer to a etimelist record with engineID <engineID> -OR- * NULL if no record exists. * * * Search etimelist for an entry with engineID. * * ASSUMES that no engineID will have more than one record in the list. */ Enginetime search_enginetime_list(u_char * engineID, u_int engineID_len) { int rval = SNMPERR_SUCCESS; Enginetime e = NULL; /* * Sanity check. */ if (!engineID || (engineID_len <= 0)) { QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit); } /* * Find the entry for engineID if there be one. */ rval = hash_engineID(engineID, engineID_len); if (rval < 0) { QUITFUN(SNMPERR_GENERR, search_enginetime_list_quit); } e = etimelist[rval]; for ( /*EMPTY*/; e; e = e->next) { if ((engineID_len == e->engineID_len) && !memcmp(e->engineID, engineID, engineID_len)) { break; } } search_enginetime_list_quit: return e; } /* end search_enginetime_list() */
/* * Create a new row in the trigger table */ netsnmp_tdata_row * mteTrigger_createEntry(const char *mteOwner, char *mteTName, int fixed) { struct mteTrigger *entry; netsnmp_tdata_row *row; size_t mteOwner_len = (mteOwner) ? strlen(mteOwner) : 0; size_t mteTName_len = (mteTName) ? strlen(mteTName) : 0; DEBUGMSGTL(("disman:event:table", "Create trigger entry (%s, %s)\n", mteOwner, mteTName)); /* * Create the mteTrigger entry, and the * (table-independent) row wrapper structure... */ entry = SNMP_MALLOC_TYPEDEF(struct mteTrigger); if (!entry) return NULL; row = netsnmp_tdata_create_row(); if (!row) { SNMP_FREE(entry); return NULL; } row->data = entry; /* * ... initialize this row with the indexes supplied * and the default values for the row... */ if (mteOwner) memcpy(entry->mteOwner, mteOwner, mteOwner_len); netsnmp_table_row_add_index(row, ASN_OCTET_STR, entry->mteOwner, mteOwner_len); if (mteTName) memcpy(entry->mteTName, mteTName, mteTName_len); netsnmp_table_row_add_index(row, ASN_PRIV_IMPLIED_OCTET_STR, entry->mteTName, mteTName_len); /* entry->mteTriggerTest = MTE_TRIGGER_BOOLEAN; */ entry->mteTriggerValueID_len = 2; /* .0.0 */ entry->mteTriggerFrequency = 600; memcpy(entry->mteDeltaDiscontID, _sysUpTime_instance, sizeof(_sysUpTime_instance)); entry->mteDeltaDiscontID_len = _sysUpTime_inst_len; entry->mteDeltaDiscontIDType = MTE_DELTAD_TTICKS; entry->flags |= MTE_TRIGGER_FLAG_SYSUPT; entry->mteTExTest = (MTE_EXIST_PRESENT | MTE_EXIST_ABSENT); entry->mteTExStartup = (MTE_EXIST_PRESENT | MTE_EXIST_ABSENT); entry->mteTBoolComparison = MTE_BOOL_UNEQUAL; entry->flags |= MTE_TRIGGER_FLAG_BSTART; entry->mteTThStartup = MTE_THRESH_START_RISEFALL; if (fixed) entry->flags |= MTE_TRIGGER_FLAG_FIXED; /* * ... and insert the row into the (common) table container */ netsnmp_tdata_add_row(trigger_table_data, row); DEBUGMSGTL(("disman:event:table", "Trigger entry created\n")); return row; }
/* implements the table_iterator helper */ int netsnmp_table_iterator_helper_handler(netsnmp_mib_handler *handler, netsnmp_handler_registration *reginfo, netsnmp_agent_request_info *reqinfo, netsnmp_request_info *requests) { netsnmp_table_registration_info *tbl_info; netsnmp_table_request_info *table_info = NULL; oid coloid[MAX_OID_LEN]; size_t coloid_len; int ret = SNMP_ERR_NOERROR; static oid myname[MAX_OID_LEN]; size_t myname_len; int oldmode = 0; netsnmp_iterator_info *iinfo; int notdone; int hintok = 0; netsnmp_request_info *request, *reqtmp = NULL; netsnmp_variable_list *index_search = NULL; netsnmp_variable_list *free_this_index_search = NULL; void *callback_loop_context = NULL, *last_loop_context; void *callback_data_context = NULL; ti_cache_info *ti_info = NULL; int request_count = 0; netsnmp_oid_stash_node **cinfo = NULL; netsnmp_variable_list *old_indexes = NULL, *vb; netsnmp_table_registration_info *table_reg_info = NULL; int i; netsnmp_data_list *ldata = NULL; iinfo = (netsnmp_iterator_info *) handler->myvoid; if (!iinfo || !reginfo || !reqinfo) return SNMP_ERR_GENERR; tbl_info = iinfo->table_reginfo; /* * copy in the table registration oid for later use */ coloid_len = reginfo->rootoid_len + 2; memcpy(coloid, reginfo->rootoid, reginfo->rootoid_len * sizeof(oid)); coloid[reginfo->rootoid_len] = 1; /* table.entry node */ /* * illegally got here if these functions aren't defined */ if (iinfo->get_first_data_point == NULL || iinfo->get_next_data_point == NULL) { snmp_log(LOG_ERR, "table_iterator helper called without data accessor functions\n"); return SNMP_ERR_GENERR; } /* preliminary analysis */ switch (reqinfo->mode) { case MODE_GET_STASH: cinfo = netsnmp_extract_stash_cache(reqinfo); table_reg_info = netsnmp_find_table_registration_info(reginfo); /* XXX: move this malloc to stash_cache handler? */ reqtmp = SNMP_MALLOC_TYPEDEF(netsnmp_request_info); if (reqtmp == NULL) return SNMP_ERR_GENERR; reqtmp->subtree = requests->subtree; table_info = netsnmp_extract_table_info(requests); netsnmp_request_add_list_data(reqtmp, netsnmp_create_data_list (TABLE_HANDLER_NAME, (void *) table_info, NULL)); /* remember the indexes that were originally parsed. */ old_indexes = table_info->indexes; break; case MODE_GETNEXT: for(request = requests ; request; request = request->next) { if (request->processed) continue; table_info = netsnmp_extract_table_info(request); if (table_info == NULL) { /* * Cleanup */ if (free_this_index_search) snmp_free_varbind(free_this_index_search); return SNMP_ERR_GENERR; } if (table_info->colnum < tbl_info->min_column - 1) { /* XXX: optimize better than this */ /* for now, just increase to colnum-1 */ /* we need to jump to the lowest result of the min_column and take it, comparing to nothing from the request */ table_info->colnum = tbl_info->min_column - 1; } else if (table_info->colnum > tbl_info->max_column) { request->processed = TABLE_ITERATOR_NOTAGAIN; } ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); if (!ti_info) { ti_info = SNMP_MALLOC_TYPEDEF(ti_cache_info); if (ti_info == NULL) { /* * Cleanup */ if (free_this_index_search) snmp_free_varbind(free_this_index_search); return SNMP_ERR_GENERR; } netsnmp_request_add_list_data(request, netsnmp_create_data_list (TI_REQUEST_CACHE, ti_info, netsnmp_free_ti_cache)); } /* XXX: if no valid requests, don't even loop below */ } break; } /* * collect all information for each needed row */ if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_GET_STASH || reqinfo->mode == MODE_SET_RESERVE1) { /* * Count the number of request in the list, * so that we'll know when we're finished */ for(request = requests ; request; request = request->next) if (!request->processed) request_count++; notdone = 1; hintok = 1; while(notdone) { notdone = 0; /* find first data point */ if (!index_search) { if (free_this_index_search) { /* previously done */ index_search = free_this_index_search; } else { for(request=requests ; request; request=request->next) { table_info = netsnmp_extract_table_info(request); if (table_info) break; } if (!table_info) { snmp_log(LOG_WARNING, "no valid requests for iterator table %s\n", reginfo->handlerName); netsnmp_free_request_data_sets(reqtmp); SNMP_FREE(reqtmp); return SNMP_ERR_NOERROR; } index_search = snmp_clone_varbind(table_info->indexes); free_this_index_search = index_search; /* setup, malloc search data: */ if (!index_search) { /* * hmmm.... invalid table? */ snmp_log(LOG_WARNING, "invalid index list or failed malloc for table %s\n", reginfo->handlerName); netsnmp_free_request_data_sets(reqtmp); SNMP_FREE(reqtmp); return SNMP_ERR_NOERROR; } } } /* if sorted, pass in a hint */ if (hintok && (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED)) { callback_loop_context = table_info; } index_search = (iinfo->get_first_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); /* loop over each data point */ while(index_search) { /* remember to free this later */ free_this_index_search = index_search; /* compare against each request*/ for(request = requests ; request; request = request->next) { if (request->processed) continue; /* XXX: store in an array for faster retrival */ table_info = netsnmp_extract_table_info(request); if (table_info == NULL) { /* * Cleanup */ if (free_this_index_search) snmp_free_varbind(free_this_index_search); return SNMP_ERR_GENERR; } coloid[reginfo->rootoid_len + 1] = table_info->colnum; ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); switch(reqinfo->mode) { case MODE_GET: case MODE_SET_RESERVE1: /* looking for exact matches */ build_oid_noalloc(myname, MAX_OID_LEN, &myname_len, coloid, coloid_len, index_search); if (snmp_oid_compare(myname, myname_len, request->requestvb->name, request->requestvb->name_length) == 0) { /* * keep this */ if (netsnmp_iterator_remember(request, myname, myname_len, callback_data_context, callback_loop_context, iinfo) == NULL) { /* * Cleanup */ if (free_this_index_search) snmp_free_varbind (free_this_index_search); return SNMP_ERR_GENERR; } request_count--; /* One less to look for */ } else { if (iinfo->free_data_context && callback_data_context) { (iinfo->free_data_context)(callback_data_context, iinfo); } } break; case MODE_GET_STASH: /* collect data for each column for every row */ build_oid_noalloc(myname, MAX_OID_LEN, &myname_len, coloid, coloid_len, index_search); reqinfo->mode = MODE_GET; if (reqtmp) ldata = netsnmp_get_list_node(reqtmp->parent_data, TABLE_ITERATOR_NAME); if (!ldata) { netsnmp_request_add_list_data(reqtmp, netsnmp_create_data_list (TABLE_ITERATOR_NAME, callback_data_context, NULL)); } else { /* may have changed */ ldata->data = callback_data_context; } table_info->indexes = index_search; for(i = table_reg_info->min_column; i <= (int)table_reg_info->max_column; i++) { myname[reginfo->rootoid_len + 1] = i; table_info->colnum = i; vb = reqtmp->requestvb = SNMP_MALLOC_TYPEDEF(netsnmp_variable_list); if (vb == NULL) { /* * Cleanup */ if (free_this_index_search) snmp_free_varbind (free_this_index_search); return SNMP_ERR_GENERR; } vb->type = ASN_NULL; snmp_set_var_objid(vb, myname, myname_len); netsnmp_call_next_handler(handler, reginfo, reqinfo, reqtmp); reqtmp->requestvb = NULL; reqtmp->processed = 0; if (vb->type != ASN_NULL) { /* XXX, not all */ netsnmp_oid_stash_add_data(cinfo, myname, myname_len, vb); } else { snmp_free_var(vb); } } reqinfo->mode = MODE_GET_STASH; break; case MODE_GETNEXT: /* looking for "next" matches */ if (netsnmp_check_getnext_reply (request, coloid, coloid_len, index_search, &ti_info->results)) { if (netsnmp_iterator_remember(request, ti_info-> results->name, ti_info-> results-> name_length, callback_data_context, callback_loop_context, iinfo) == NULL) { /* * Cleanup */ if (free_this_index_search) snmp_free_varbind (free_this_index_search); return SNMP_ERR_GENERR; } /* * If we've been told that the rows are sorted, * then the first valid one we find * must be the right one. */ if (iinfo->flags & NETSNMP_ITERATOR_FLAG_SORTED) request_count--; } else { if (iinfo->free_data_context && callback_data_context) { (iinfo->free_data_context)(callback_data_context, iinfo); } } break; case MODE_SET_RESERVE2: case MODE_SET_FREE: case MODE_SET_UNDO: case MODE_SET_COMMIT: /* needed processing already done in RESERVE1 */ break; default: snmp_log(LOG_ERR, "table_iterator called with unsupported mode\n"); break; /* XXX return */ } } /* Is there any point in carrying on? */ if (!request_count) break; /* get the next search possibility */ last_loop_context = callback_loop_context; index_search = (iinfo->get_next_data_point) (&callback_loop_context, &callback_data_context, index_search, iinfo); if (iinfo->free_loop_context && last_loop_context && callback_data_context != last_loop_context) { (iinfo->free_loop_context) (last_loop_context, iinfo); last_loop_context = NULL; } } /* free loop context before going on */ if (callback_loop_context && iinfo->free_loop_context_at_end) { (iinfo->free_loop_context_at_end) (callback_loop_context, iinfo); callback_loop_context = NULL; } /* decide which (GETNEXT) requests are not yet filled */ if (reqinfo->mode == MODE_GETNEXT) { for(request = requests ; request; request = request->next) { if (request->processed) continue; ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); if (!ti_info->results) { int nc; table_info = netsnmp_extract_table_info(request); nc = netsnmp_table_next_column(table_info); if (0 == nc) { coloid[reginfo->rootoid_len+1] = table_info->colnum+1; snmp_set_var_objid(request->requestvb, coloid, reginfo->rootoid_len+2); request->processed = TABLE_ITERATOR_NOTAGAIN; break; } else { table_info->colnum = nc; hintok = 0; notdone = 1; } } } } } } if (reqinfo->mode == MODE_GET || reqinfo->mode == MODE_GETNEXT || reqinfo->mode == MODE_SET_RESERVE1) { /* per request last minute processing */ for(request = requests ; request; request = request->next) { if (request->processed) continue; ti_info = netsnmp_request_get_list_data(request, TI_REQUEST_CACHE); table_info = netsnmp_extract_table_info(request); if (!ti_info) continue; switch(reqinfo->mode) { case MODE_GETNEXT: if (ti_info->best_match_len) snmp_set_var_objid(request->requestvb, ti_info->best_match, ti_info->best_match_len); else { coloid[reginfo->rootoid_len+1] = netsnmp_table_next_column(table_info); if (0 == coloid[reginfo->rootoid_len+1]) { /* out of range. */ coloid[reginfo->rootoid_len+1] = tbl_info->max_column + 1; request->processed = TABLE_ITERATOR_NOTAGAIN; } snmp_set_var_objid(request->requestvb, coloid, reginfo->rootoid_len+2); request->processed = 1; } snmp_free_varbind(table_info->indexes); table_info->indexes = snmp_clone_varbind(ti_info->results); /* FALL THROUGH */ case MODE_GET: case MODE_SET_RESERVE1: if (ti_info->data_context) /* we don't add a free pointer, since it's in the TI_REQUEST_CACHE instead */ netsnmp_request_add_list_data(request, netsnmp_create_data_list (TABLE_ITERATOR_NAME, ti_info->data_context, NULL)); break; default: break; } } /* we change all GETNEXT operations into GET operations. why? because we're just so nice to the lower levels. maybe someday they'll pay us for it. doubtful though. */ oldmode = reqinfo->mode; if (reqinfo->mode == MODE_GETNEXT) { reqinfo->mode = MODE_GET; } } else if (reqinfo->mode == MODE_GET_STASH) { netsnmp_free_request_data_sets(reqtmp); SNMP_FREE(reqtmp); table_info->indexes = old_indexes; } /* Finally, we get to call the next handler below us. Boy, wasn't all that simple? They better be glad they don't have to do it! */ if (reqinfo->mode != MODE_GET_STASH) { DEBUGMSGTL(("table_iterator", "call subhandler for mode: %s\n", se_find_label_in_slist("agent_mode", oldmode))); ret = netsnmp_call_next_handler(handler, reginfo, reqinfo, requests); } /* reverse the previously saved mode if we were a getnext */ if (oldmode == MODE_GETNEXT) { reqinfo->mode = oldmode; } /* cleanup */ if (free_this_index_search) snmp_free_varbind(free_this_index_search); return ret; }
void devla_getstats(unsigned int regno, void *dummy) { static struct statinfo *lastat = NULL; int i; double busy_time, busy_percent; static double expon1, expon5, expon15; char current_name[DEVSTAT_NAME_LEN+5]; if (lastat == NULL) { lastat = (struct statinfo *) malloc(sizeof(struct statinfo)); if (lastat != NULL) lastat->dinfo = (struct devinfo *) calloc(sizeof(struct devinfo), 1); if (lastat == NULL || lastat->dinfo == NULL) { SNMP_FREE(lastat); ERROR_MSG("Memory alloc failure - devla_getstats()\n"); return; } } if ((GETDEVS(lastat)) == -1) { ERROR_MSG("can't do getdevs()\n"); return; } if (ndevs != 0) { for (i=0; i < ndevs; i++) { snprintf(current_name, sizeof(current_name), "%s%d", lastat->dinfo->devices[i].device_name, lastat->dinfo->devices[i].unit_number); if (strcmp(current_name, devloads[i].name)) { ndevs = 0; free(devloads); } } } if (ndevs == 0) { ndevs = lastat->dinfo->numdevs; devloads = (struct dev_la *) malloc(ndevs * sizeof(struct dev_la)); memset(devloads, '\0', ndevs * sizeof(struct dev_la)); for (i=0; i < ndevs; i++) { devloads[i].la1 = devloads[i].la5 = devloads[i].la15 = 0; memcpy(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time, sizeof(devloads[i].prev)); snprintf(devloads[i].name, sizeof(devloads[i].name), "%s%d", lastat->dinfo->devices[i].device_name, lastat->dinfo->devices[i].unit_number); } expon1 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)60))); expon5 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)300))); expon15 = exp(-(((double)DISKIO_SAMPLE_INTERVAL) / ((double)900))); } for (i=0; i<ndevs; i++) { #if defined(freebsd5) && __FreeBSD_version >= 500107 busy_time = devstat_compute_etime(&lastat->dinfo->devices[i].busy_time, &devloads[i].prev); #else busy_time = devla_timeval_diff(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time); #endif if ( busy_time < 0 ) busy_time = 0; /* Account for possible FP loss of precision near zero */ busy_percent = busy_time * 100 / DISKIO_SAMPLE_INTERVAL; devloads[i].la1 = devloads[i].la1 * expon1 + busy_percent * (1 - expon1); /* fprintf(stderr, "(%d) %s: update la1=%.2lf%%\n", i, devloads[i].name, expon1); */ devloads[i].la5 = devloads[i].la5 * expon5 + busy_percent * (1 - expon5); devloads[i].la15 = devloads[i].la15 * expon15 + busy_percent * (1 - expon15); memcpy(&devloads[i].prev, &lastat->dinfo->devices[i].busy_time, sizeof(devloads[i].prev)); } }
main(int argc, char *argv[]) #endif { char options[128] = "aAc:CdD::efF:g:hHI:L:m:M:no:O:PqsS:tu:vx:-:"; netsnmp_session *sess_list = NULL, *ss = NULL; netsnmp_transport *transport = NULL; int arg, i = 0; int uid = 0, gid = 0; char *cp, *listen_ports = NULL; #if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(NETSNMP_SNMPTRAPD_DISABLE_AGENTX) int agentx_subagent = 1; #endif netsnmp_trapd_handler *traph; #ifndef WIN32 /* * close all non-standard file descriptors we may have * inherited from the shell. */ for (i = getdtablesize() - 1; i > 2; --i) { (void) close(i); } #endif /* #WIN32 */ #ifdef SIGTERM signal(SIGTERM, term_handler); #endif #ifdef SIGHUP signal(SIGHUP, SIG_IGN); /* do not terminate on early SIGHUP */ #endif #ifdef SIGINT signal(SIGINT, term_handler); #endif #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); /* 'Inline' failure of wayward readers */ #endif /* * register our configuration handlers now so -H properly displays them */ snmptrapd_register_configs( ); #ifdef NETSNMP_USE_MYSQL snmptrapd_register_sql_configs( ); #endif #ifdef NETSNMP_SECMOD_USM init_usm_conf( "snmptrapd" ); #endif /* NETSNMP_SECMOD_USM */ register_config_handler("snmptrapd", "snmpTrapdAddr", parse_trapd_address, free_trapd_address, "string"); register_config_handler("snmptrapd", "doNotLogTraps", parse_config_doNotLogTraps, NULL, "(1|yes|true|0|no|false)"); #if HAVE_GETPID register_config_handler("snmptrapd", "pidFile", parse_config_pidFile, NULL, "string"); #endif #ifdef HAVE_UNISTD_H register_config_handler("snmptrapd", "agentuser", parse_config_agentuser, NULL, "userid"); register_config_handler("snmptrapd", "agentgroup", parse_config_agentgroup, NULL, "groupid"); #endif register_config_handler("snmptrapd", "doNotFork", parse_config_doNotFork, NULL, "(1|yes|true|0|no|false)"); register_config_handler("snmptrapd", "ignoreAuthFailure", parse_config_ignoreAuthFailure, NULL, "(1|yes|true|0|no|false)"); register_config_handler("snmptrapd", "outputOption", parse_config_outputOption, NULL, "string"); /* * Add some options if they are available. */ #if HAVE_GETPID strcat(options, "p:"); #endif #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG #ifdef WIN32 snmp_log_syslogname(app_name_long); #else snmp_log_syslogname(app_name); #endif #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ /* * Now process options normally. */ while ((arg = getopt(argc, argv, options)) != EOF) { switch (arg) { case '-': if (strcasecmp(optarg, "help") == 0) { usage(); exit(0); } if (strcasecmp(optarg, "version") == 0) { version(); exit(0); } handle_long_opt(optarg); break; case 'a': dropauth = 1; break; case 'A': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_APPEND_LOGFILES, 1); break; case 'c': if (optarg != NULL) { netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_OPTIONALCONFIG, optarg); } else { usage(); exit(1); } break; case 'C': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_READ_CONFIGS, 1); break; case 'd': netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DUMP_PACKET, 1); break; case 'D': debug_register_tokens(optarg); snmp_set_do_debugging(1); break; case 'f': dofork = 0; break; case 'F': if (optarg != NULL) { if (( strncmp( optarg, "print", 5 ) == 0 ) || ( strncmp( optarg, "syslog", 6 ) == 0 ) || ( strncmp( optarg, "execute", 7 ) == 0 )) { /* New style: "type=format" */ trap1_fmt_str_remember = strdup(optarg); cp = strchr( trap1_fmt_str_remember, '=' ); if (cp) *cp = ' '; } else { /* Old style: implicitly "print=format" */ trap1_fmt_str_remember = malloc(strlen(optarg) + 7); sprintf( trap1_fmt_str_remember, "print %s", optarg ); } } else { usage(); exit(1); } break; #if HAVE_UNISTD_H case 'g': if (optarg != NULL) { netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID, gid = atoi(optarg)); } else { usage(); exit(1); } break; #endif case 'h': usage(); exit(0); case 'H': init_agent("snmptrapd"); #ifdef USING_NOTIFICATION_LOG_MIB_NOTIFICATION_LOG_MODULE init_notification_log(); #endif #ifdef NETSNMP_EMBEDDED_PERL init_perl(); #endif init_snmp("snmptrapd"); fprintf(stderr, "Configuration directives understood:\n"); read_config_print_usage(" "); exit(0); case 'I': if (optarg != NULL) { add_to_init_list(optarg); } else { usage(); } break; case 'S': fprintf(stderr, "Warning: -S option has been withdrawn; use -Ls <facility> instead\n"); exit(1); break; case 'm': if (optarg != NULL) { setenv("MIBS", optarg, 1); } else { usage(); exit(1); } break; case 'M': if (optarg != NULL) { setenv("MIBDIRS", optarg, 1); } else { usage(); exit(1); } break; case 'n': netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_APP_NUMERIC_IP, 1); break; case 'o': fprintf(stderr, "Warning: -o option has been withdrawn; use -Lf <file> instead\n"); exit(1); break; case 'O': cp = snmp_out_toggle_options(optarg); if (cp != NULL) { fprintf(stderr, "Unknown output option passed to -O: %c\n", *cp); usage(); exit(1); } break; case 'L': if (snmp_log_options( optarg, argc, argv ) < 0 ) { usage(); exit(1); } break; #if HAVE_GETPID case 'p': if (optarg != NULL) { parse_config_pidFile(NULL, optarg); } else { usage(); exit(1); } break; #endif case 'P': fprintf(stderr, "Warning: -P option has been withdrawn; use -f -Le instead\n"); exit(1); break; case 's': fprintf(stderr, "Warning: -s option has been withdrawn; use -Lsd instead\n"); exit(1); break; case 't': SyslogTrap++; break; #if HAVE_UNISTD_H case 'u': if (optarg != NULL) { char *ecp; uid = strtoul(optarg, &ecp, 10); #if HAVE_GETPWNAM && HAVE_PWD_H if (*ecp) { struct passwd *info; info = getpwnam(optarg); uid = info ? info->pw_uid : -1; endpwent(); } #endif if (uid < 0) { fprintf(stderr, "Bad user id: %s\n", optarg); exit(1); } netsnmp_ds_set_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID, uid); } else { usage(); exit(1); } break; #endif case 'v': version(); exit(0); break; case 'x': if (optarg != NULL) { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_X_SOCKET, optarg); } else { usage(); exit(1); } break; default: fprintf(stderr, "invalid option: -%c\n", arg); usage(); exit(1); break; } } if (optind < argc) { /* * There are optional transport addresses on the command line. */ for (i = optind; i < argc; i++) { char *astring; if (listen_ports != NULL) { astring = malloc(strlen(listen_ports) + 2 + strlen(argv[i])); if (astring == NULL) { fprintf(stderr, "malloc failure processing argv[%d]\n", i); exit(1); } sprintf(astring, "%s,%s", listen_ports, argv[i]); free(listen_ports); listen_ports = astring; } else { listen_ports = strdup(argv[i]); if (listen_ports == NULL) { fprintf(stderr, "malloc failure processing argv[%d]\n", i); exit(1); } } } } SOCK_STARTUP; /* * I'm being lazy here, and not checking the * return value from these registration calls. * Don't try this at home, children! */ if (0 == snmp_get_do_logging()) { #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_PRE_HANDLER, syslog_handler); traph->authtypes = TRAP_AUTH_LOG; snmp_enable_syslog(); #else /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_STDIO traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_PRE_HANDLER, print_handler); traph->authtypes = TRAP_AUTH_LOG; snmp_enable_stderr(); #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_STDIO */ #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ } else { traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_PRE_HANDLER, print_handler); traph->authtypes = TRAP_AUTH_LOG; } #if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(NETSNMP_SNMPTRAPD_DISABLE_AGENTX) /* * we're an agentx subagent? */ if (agentx_subagent) { /* * make us a agentx client. */ netsnmp_ds_set_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_ROLE, 1); } #endif /* * don't fail if we can't do agentx (ie, socket not there, or not root) */ netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS); /* * ignore any warning messages. */ netsnmp_ds_toggle_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_CONNECTION_WARNINGS); /* * initialize the agent library */ init_agent("snmptrapd"); #if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(NETSNMP_SNMPTRAPD_DISABLE_AGENTX) #ifdef NETSNMP_FEATURE_CHECKING netsnmp_feature_require(register_snmpEngine_scalars_context) #endif /* NETSNMP_FEATURE_CHECKING */ /* * initialize local modules */ if (agentx_subagent) { #ifdef USING_SNMPV3_SNMPENGINE_MODULE extern void register_snmpEngine_scalars_context(const char *); #endif subagent_init(); #ifdef USING_NOTIFICATION_LOG_MIB_NOTIFICATION_LOG_MODULE /* register the notification log table */ if (should_init("notificationLogMib")) { netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_NOTIF_LOG_CTX, "snmptrapd"); traph = netsnmp_add_global_traphandler(NETSNMPTRAPD_POST_HANDLER, notification_handler); traph->authtypes = TRAP_AUTH_LOG; init_notification_log(); } #endif #ifdef USING_SNMPV3_SNMPENGINE_MODULE /* * register scalars from SNMP-FRAMEWORK-MIB::snmpEngineID group; * allows engineID probes via the master agent under the * snmptrapd context */ register_snmpEngine_scalars_context("snmptrapd"); #endif } #endif /* USING_AGENTX_SUBAGENT_MODULE && !NETSNMP_SNMPTRAPD_DISABLE_AGENTX */ /* register our authorization handler */ init_netsnmp_trapd_auth(); #if defined(USING_AGENTX_SUBAGENT_MODULE) && !defined(NETSNMP_SNMPTRAPD_DISABLE_AGENTX) if (agentx_subagent) { #ifdef USING_AGENT_NSVACMACCESSTABLE_MODULE extern void init_register_nsVacm_context(const char *); #endif #ifdef USING_SNMPV3_USMUSER_MODULE #ifdef NETSNMP_FEATURE_CHECKING netsnmp_feature_require(init_register_usmUser_context) #endif /* NETSNMP_FEATURE_CHECKING */ extern void init_register_usmUser_context(const char *); /* register ourselves as having a USM user database */ init_register_usmUser_context("snmptrapd"); #endif #ifdef USING_AGENT_NSVACMACCESSTABLE_MODULE /* register net-snmp vacm extensions */ init_register_nsVacm_context("snmptrapd"); #endif #ifdef USING_TLSTM_MIB_SNMPTLSTMCERTTOTSNTABLE_MODULE init_snmpTlstmCertToTSNTable_context("snmptrapd"); #endif } #endif #ifdef NETSNMP_EMBEDDED_PERL init_perl(); { /* set the default path to load */ char init_file[SNMP_MAXBUF]; snprintf(init_file, sizeof(init_file) - 1, "%s/%s", SNMPSHAREPATH, "snmp_perl_trapd.pl"); netsnmp_ds_set_string(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_PERL_INIT_FILE, init_file); } #endif /* * Initialize the world. */ init_snmp("snmptrapd"); #ifdef SIGHUP signal(SIGHUP, hup_handler); #endif if (trap1_fmt_str_remember) { parse_format( NULL, trap1_fmt_str_remember ); } if (netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_QUIT_IMMEDIATELY)) { /* * just starting up to process specific configuration and then * shutting down immediately. */ netsnmp_running = 0; } /* * if no logging options on command line or in conf files, use syslog */ if (0 == snmp_get_do_logging()) { #ifndef NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG #ifdef WIN32 snmp_enable_syslog_ident(app_name_long, Facility); #else snmp_enable_syslog_ident(app_name, Facility); #endif #endif /* NETSNMP_FEATURE_REMOVE_LOGGING_SYSLOG */ } if (listen_ports) cp = listen_ports; else cp = default_port; while (cp != NULL) { char *sep = strchr(cp, ','); if (sep != NULL) { *sep = 0; } transport = netsnmp_transport_open_server("snmptrap", cp); if (transport == NULL) { snmp_log(LOG_ERR, "couldn't open %s -- errno %d (\"%s\")\n", cp, errno, strerror(errno)); snmptrapd_close_sessions(sess_list); SOCK_CLEANUP; exit(1); } else { ss = snmptrapd_add_session(transport); if (ss == NULL) { /* * Shouldn't happen? We have already opened the transport * successfully so what could have gone wrong? */ snmptrapd_close_sessions(sess_list); netsnmp_transport_free(transport); snmp_log(LOG_ERR, "couldn't open snmp - %s", strerror(errno)); SOCK_CLEANUP; exit(1); } else { ss->next = sess_list; sess_list = ss; } } /* * Process next listen address, if there is one. */ if (sep != NULL) { *sep = ','; cp = sep + 1; } else { cp = NULL; } } SNMP_FREE(listen_ports); /* done with them */ #ifdef NETSNMP_USE_MYSQL if( netsnmp_mysql_init() ) { fprintf(stderr, "MySQL initialization failed\n"); exit(1); } #endif #ifndef WIN32 /* * fork the process to the background if we are not printing to stderr */ if (dofork && netsnmp_running) { int fd; switch (fork()) { case -1: fprintf(stderr, "bad fork - %s\n", strerror(errno)); _exit(1); case 0: /* * become process group leader */ if (setsid() == -1) { fprintf(stderr, "bad setsid - %s\n", strerror(errno)); _exit(1); } /* * if we are forked, we don't want to print out to stdout or stderr */ fd = open("/dev/null", O_RDWR); dup2(fd, STDIN_FILENO); dup2(fd, STDOUT_FILENO); dup2(fd, STDERR_FILENO); close(fd); break; default: _exit(0); } } #endif /* WIN32 */ #if HAVE_GETPID if (pid_file != NULL) { if ((PID = fopen(pid_file, "w")) == NULL) { snmp_log_perror("fopen"); exit(1); } fprintf(PID, "%d\n", (int) getpid()); fclose(PID); free_config_pidFile(); } #endif snmp_log(LOG_INFO, "NET-SNMP version %s\n", netsnmp_get_version()); /* * ignore early sighup during startup */ reconfig = 0; #if HAVE_UNISTD_H #ifdef HAVE_SETGID if ((gid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_GROUPID)) != 0) { DEBUGMSGTL(("snmptrapd/main", "Changing gid to %d.\n", gid)); if (setgid(gid) == -1 #ifdef HAVE_SETGROUPS || setgroups(1, (gid_t *)&gid) == -1 #endif ) { snmp_log_perror("setgid failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif #ifdef HAVE_SETUID if ((uid = netsnmp_ds_get_int(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_USERID)) != 0) { DEBUGMSGTL(("snmptrapd/main", "Changing uid to %d.\n", uid)); if (setuid(uid) == -1) { snmp_log_perror("setuid failed"); if (!netsnmp_ds_get_boolean(NETSNMP_DS_APPLICATION_ID, NETSNMP_DS_AGENT_NO_ROOT_ACCESS)) { exit(1); } } } #endif #endif #ifdef WIN32SERVICE trapd_status = SNMPTRAPD_RUNNING; #endif snmptrapd_main_loop(); if (snmp_get_do_logging()) { struct tm *tm; time_t timer; time(&timer); tm = localtime(&timer); snmp_log(LOG_INFO, "%.4d-%.2d-%.2d %.2d:%.2d:%.2d NET-SNMP version %s Stopped.\n", tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, netsnmp_get_version()); } snmp_log(LOG_INFO, "Stopping snmptrapd\n"); #ifdef NETSNMP_EMBEDDED_PERL shutdown_perl(); #endif snmptrapd_close_sessions(sess_list); snmp_shutdown("snmptrapd"); #ifdef WIN32SERVICE trapd_status = SNMPTRAPD_STOPPED; #endif snmp_disable_log(); SOCK_CLEANUP; return 0; }
int snmpv3_options(char *optarg, netsnmp_session * session, char **Apsz, char **Xpsz, int argc, char *const *argv) { char *cp = optarg; int testcase; optarg++; /* * Support '... -3x=value ....' syntax */ if (*optarg == '=') { optarg++; } /* * and '.... "-3x value" ....' (*with* the quotes) */ while (*optarg && isspace(*optarg)) { optarg++; } /* * Finally, handle ".... -3x value ...." syntax * (*without* surrounding quotes) */ if (!*optarg) { /* * We've run off the end of the argument * so move on the the next. */ optarg = argv[optind++]; if (optind > argc) { fprintf(stderr, "Missing argument after SNMPv3 '-3%c' option.\n", *cp); return (-1); } } switch (*cp) { case 'Z': session->engineBoots = strtoul(optarg, NULL, 10); if (session->engineBoots == 0 || !isdigit(optarg[0])) { fprintf(stderr, "Need engine boots value after -3Z flag.\n"); return (-1); } cp = strchr(optarg, ','); if (cp && *(++cp) && isdigit(*cp)) session->engineTime = strtoul(cp, NULL, 10); else { fprintf(stderr, "Need engine time value after -3Z flag.\n"); return (-1); } break; case 'e':{ size_t ebuf_len = 32, eout_len = 0; u_char *ebuf = (u_char *) malloc(ebuf_len); if (ebuf == NULL) { fprintf(stderr, "malloc failure processing -3e flag.\n"); return (-1); } if (!snmp_hex_to_binary (&ebuf, &ebuf_len, &eout_len, 1, optarg)) { fprintf(stderr, "Bad engine ID value after -3e flag.\n"); SNMP_FREE(ebuf); return (-1); } session->securityEngineID = ebuf; session->securityEngineIDLen = eout_len; break; } case 'E':{ size_t ebuf_len = 32, eout_len = 0; u_char *ebuf = (u_char *) malloc(ebuf_len); if (ebuf == NULL) { fprintf(stderr, "malloc failure processing -3E flag.\n"); return (-1); } if (!snmp_hex_to_binary (&ebuf, &ebuf_len, &eout_len, 1, optarg)) { fprintf(stderr, "Bad engine ID value after -3E flag.\n"); SNMP_FREE(ebuf); return (-1); } session->contextEngineID = ebuf; session->contextEngineIDLen = eout_len; break; } case 'n': session->contextName = optarg; session->contextNameLen = strlen(optarg); break; case 'u': session->securityName = optarg; session->securityNameLen = strlen(optarg); break; case 'l': if (!strcasecmp(optarg, "noAuthNoPriv") || !strcmp(optarg, "1") || !strcasecmp(optarg, "nanp")) { session->securityLevel = SNMP_SEC_LEVEL_NOAUTH; } else if (!strcasecmp(optarg, "authNoPriv") || !strcmp(optarg, "2") || !strcasecmp(optarg, "anp")) { session->securityLevel = SNMP_SEC_LEVEL_AUTHNOPRIV; } else if (!strcasecmp(optarg, "authPriv") || !strcmp(optarg, "3") || !strcasecmp(optarg, "ap")) { session->securityLevel = SNMP_SEC_LEVEL_AUTHPRIV; } else { fprintf(stderr, "Invalid security level specified after -3l flag: %s\n", optarg); return (-1); } break; case 'a': #ifndef DISABLE_MD5 if (!strcasecmp(optarg, "MD5")) { session->securityAuthProto = usmHMACMD5AuthProtocol; session->securityAuthProtoLen = USM_AUTH_PROTO_MD5_LEN; } else #endif if (!strcasecmp(optarg, "SHA")) { session->securityAuthProto = usmHMACSHA1AuthProtocol; session->securityAuthProtoLen = USM_AUTH_PROTO_SHA_LEN; } else { fprintf(stderr, "Invalid authentication protocol specified after -3a flag: %s\n", optarg); return (-1); } break; case 'x': testcase = 0; #ifndef DISABLE_DES if (!strcasecmp(optarg, "DES")) { session->securityPrivProto = usmDESPrivProtocol; session->securityPrivProtoLen = USM_PRIV_PROTO_DES_LEN; testcase = 1; } #endif #ifdef HAVE_AES if (!strcasecmp(optarg, "AES128") || strcasecmp(optarg, "AES")) { session->securityPrivProto = usmAES128PrivProtocol; session->securityPrivProtoLen = USM_PRIV_PROTO_AES128_LEN; testcase = 1; } #endif if (testcase == 0) { fprintf(stderr, "Invalid privacy protocol specified after -3x flag: %s\n", optarg); return (-1); } break; case 'A': *Apsz = optarg; break; case 'X': *Xpsz = optarg; break; case 'm': { size_t bufSize = sizeof(session->securityAuthKey); u_char *tmpp = session->securityAuthKey; if (!snmp_hex_to_binary(&tmpp, &bufSize, &session->securityAuthKeyLen, 0, optarg)) { fprintf(stderr, "Bad key value after -3m flag.\n"); return (-1); } break; } case 'M': { size_t bufSize = sizeof(session->securityPrivKey); u_char *tmpp = session->securityPrivKey; if (!snmp_hex_to_binary(&tmpp, &bufSize, &session->securityPrivKeyLen, 0, optarg)) { fprintf(stderr, "Bad key value after -3M flag.\n"); return (-1); } break; } case 'k': { size_t kbuf_len = 32, kout_len = 0; u_char *kbuf = (u_char *) malloc(kbuf_len); if (kbuf == NULL) { fprintf(stderr, "malloc failure processing -3k flag.\n"); return (-1); } if (!snmp_hex_to_binary (&kbuf, &kbuf_len, &kout_len, 1, optarg)) { fprintf(stderr, "Bad key value after -3k flag.\n"); SNMP_FREE(kbuf); return (-1); } session->securityAuthLocalKey = kbuf; session->securityAuthLocalKeyLen = kout_len; break; } case 'K': { size_t kbuf_len = 32, kout_len = 0; u_char *kbuf = (u_char *) malloc(kbuf_len); if (kbuf == NULL) { fprintf(stderr, "malloc failure processing -3K flag.\n"); return (-1); } if (!snmp_hex_to_binary (&kbuf, &kbuf_len, &kout_len, 1, optarg)) { fprintf(stderr, "Bad key value after -3K flag.\n"); SNMP_FREE(kbuf); return (-1); } session->securityPrivLocalKey = kbuf; session->securityPrivLocalKeyLen = kout_len; break; } default: fprintf(stderr, "Unknown SNMPv3 option passed to -3: %c.\n", *cp); return -1; } return 0; }
/* * Locate the appropriate transport domain and call the create function for * it. */ netsnmp_transport * netsnmp_tdomain_transport_full(const char *application, const char *str, int local, const char *default_domain, const char *default_target) { netsnmp_tdomain *match = NULL; const char *addr = NULL; const char * const *spec = NULL; int any_found = 0; char buf[SNMP_MAXPATH]; extern const char *curfilename; /* from read_config.c */ const char *prev_curfilename; prev_curfilename = curfilename; DEBUGMSGTL(("tdomain", "tdomain_transport_full(\"%s\", \"%s\", %d, \"%s\", \"%s\")\n", application, str ? str : "[NIL]", local, default_domain ? default_domain : "[NIL]", default_target ? default_target : "[NIL]")); /* see if we can load a host-name specific set of conf files */ if (!netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_DONT_LOAD_HOST_FILES) && netsnmp_is_fqdn(str)) { static int have_added_handler = 0; char *newhost; struct config_line *config_handlers; struct config_files file_names; char *prev_hostname; /* register a "transport" specifier */ if (!have_added_handler) { have_added_handler = 1; netsnmp_ds_register_config(ASN_OCTET_STR, "snmp", "transport", NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_HOSTNAME); } /* we save on specific setting that we don't allow to change from one transport creation to the next; ie, we don't want the "transport" specifier to be a default. It should be a single invocation use only */ prev_hostname = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_HOSTNAME); if (prev_hostname) prev_hostname = strdup(prev_hostname); /* read in the hosts/STRING.conf files */ config_handlers = read_config_get_handlers("snmp"); snprintf(buf, sizeof(buf)-1, "hosts/%s", str); file_names.fileHeader = buf; file_names.start = config_handlers; file_names.next = NULL; DEBUGMSGTL(("tdomain", "checking for host specific config %s\n", buf)); read_config_files_of_type(EITHER_CONFIG, &file_names); if (NULL != (newhost = netsnmp_ds_get_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_HOSTNAME))) { strlcpy(buf, newhost, sizeof(buf)); str = buf; } netsnmp_ds_set_string(NETSNMP_DS_LIBRARY_ID, NETSNMP_DS_LIB_HOSTNAME, prev_hostname); SNMP_FREE(prev_hostname); } /* First try - assume that there is a domain in str (domain:target) */ if (str != NULL) { const char *cp; if ((cp = strchr(str, ':')) != NULL) { char* mystring = (char*)malloc(cp + 1 - str); memcpy(mystring, str, cp - str); mystring[cp - str] = '\0'; addr = cp + 1; match = find_tdomain(mystring); free(mystring); } } /* * Second try, if there is no domain in str (target), then try the * default domain */ if (match == NULL) { addr = str; if (addr && *addr == '/') { DEBUGMSGTL(("tdomain", "Address starts with '/', so assume \"unix\" " "domain\n")); match = find_tdomain("unix"); } else if (default_domain) { DEBUGMSGTL(("tdomain", "Use user specified default domain \"%s\"\n", default_domain)); match = find_tdomain(default_domain); } else { spec = netsnmp_lookup_default_domains(application); if (spec == NULL) { DEBUGMSGTL(("tdomain", "No default domain found, assume \"udp\"\n")); match = find_tdomain("udp"); } else { const char * const * r = spec; DEBUGMSGTL(("tdomain", "Use application default domains")); while(*r) { DEBUGMSG(("tdomain", " \"%s\"", *r)); ++r; } DEBUGMSG(("tdomain", "\n")); } } } for(;;) { if (match) { netsnmp_transport *t = NULL; const char* addr2; any_found = 1; /* * Ok, we know what domain to try, lets see what default data * should be used with it */ if (default_target != NULL) addr2 = default_target; else addr2 = netsnmp_lookup_default_target(application, match->prefix[0]); DEBUGMSGTL(("tdomain", "trying domain \"%s\" address \"%s\" " "default address \"%s\"\n", match->prefix[0], addr ? addr : "[NIL]", addr2 ? addr2 : "[NIL]")); if (match->f_create_from_tstring) t = match->f_create_from_tstring(addr, local); else t = match->f_create_from_tstring_new(addr, local, addr2); if (t) { curfilename = prev_curfilename; return t; } } addr = str; if (spec && *spec) match = find_tdomain(*spec++); else break; } if (!any_found) snmp_log(LOG_ERR, "No support for any checked transport domain\n"); curfilename = prev_curfilename; return NULL; }
_SCAPI_NOT_CONFIGURED #endif /* */ /*******************************************************************-o-****** * sc_generate_keyed_hash * * Parameters: * authtype Type of authentication transform. * authtypelen * *key Pointer to key (Kul) to use in keyed hash. * keylen Length of key in bytes. * *message Pointer to the message to hash. * msglen Length of the message. * *MAC Will be returned with allocated bytes containg hash. * *maclen Length of the hash buffer in bytes; also indicates * whether the MAC should be truncated. * * Returns: * SNMPERR_SUCCESS Success. * SNMPERR_GENERR All errs * * * A hash of the first msglen bytes of message using a keyed hash defined * by authtype is created and stored in MAC. MAC is ASSUMED to be a buffer * of at least maclen bytes. If the length of the hash is greater than * maclen, it is truncated to fit the buffer. If the length of the hash is * less than maclen, maclen set to the number of hash bytes generated. * * ASSUMED that the number of hash bits is a multiple of 8. */ int sc_generate_keyed_hash(const oid * authtype, size_t authtypelen, u_char * key, u_int keylen, u_char * message, u_int msglen, u_char * MAC, size_t * maclen) #if defined(USE_INTERNAL_MD5) || defined(USE_OPENSSL) { int rval = SNMPERR_SUCCESS; int properlength; u_char buf[SNMP_MAXBUF_SMALL]; #if defined(USE_OPENSSL) int buf_len = sizeof(buf); #endif DEBUGTRACE; #ifdef SNMP_TESTING_CODE { int i; DEBUGMSG(("sc_generate_keyed_hash", "sc_generate_keyed_hash(): key=0x")); for (i = 0; i < keylen; i++) DEBUGMSG(("sc_generate_keyed_hash", "%02x", key[i] & 0xff)); DEBUGMSG(("sc_generate_keyed_hash", " (%d)\n", keylen)); } #endif /* SNMP_TESTING_CODE */ /* * Sanity check. */ if (!authtype || !key || !message || !MAC || !maclen || (keylen <= 0) || (msglen <= 0) || (*maclen <= 0) || (authtypelen != USM_LENGTH_OID_TRANSFORM)) { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } properlength = sc_get_properlength(authtype, authtypelen); if (properlength == SNMPERR_GENERR) return properlength; if (((int) keylen < properlength)) { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } #ifdef USE_OPENSSL /* * Determine transform type. */ if (ISTRANSFORM(authtype, HMACMD5Auth)) HMAC(EVP_md5(), key, keylen, message, msglen, buf, &buf_len); else if (ISTRANSFORM(authtype, HMACSHA1Auth)) HMAC(EVP_sha1(), key, keylen, message, msglen, buf, &buf_len); else { QUITFUN(SNMPERR_GENERR, sc_generate_keyed_hash_quit); } if (buf_len != properlength) { QUITFUN(rval, sc_generate_keyed_hash_quit); } if (*maclen > buf_len) *maclen = buf_len; memcpy(MAC, buf, *maclen); #else if ((int) *maclen > properlength) *maclen = properlength; if (MDsign(message, msglen, MAC, *maclen, key, keylen)) { rval = SNMPERR_GENERR; goto sc_generate_keyed_hash_quit; } #endif /* USE_OPENSSL */ #ifdef SNMP_TESTING_CODE { char *s; int len = binary_to_hex(MAC, *maclen, &s); DEBUGMSGTL(("scapi", "Full v3 message hash: %s\n", s)); SNMP_ZERO(s, len); SNMP_FREE(s); } #endif sc_generate_keyed_hash_quit: SNMP_ZERO(buf, SNMP_MAXBUF_SMALL); return rval; } /* end sc_generate_keyed_hash() */