Index: stable/9/contrib/bsnmp/lib/asn1.c =================================================================== --- stable/9/contrib/bsnmp/lib/asn1.c (revision 301661) +++ stable/9/contrib/bsnmp/lib/asn1.c (revision 301662) @@ -1,1022 +1,1022 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/lib/asn1.c,v 1.31 2005/10/06 07:14:58 brandt_h Exp $ * * ASN.1 for SNMP. */ #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #include "support.h" #include "asn1.h" static void asn_error_func(const struct asn_buf *, const char *, ...); void (*asn_error)(const struct asn_buf *, const char *, ...) = asn_error_func; /* * Read the next header. This reads the tag (note, that only single * byte tags are supported for now) and the length field. The length field * is restricted to a 32-bit value. * All errors of this function stop the decoding. */ enum asn_err asn_get_header(struct asn_buf *b, u_char *type, asn_len_t *len) { u_int length; if (b->asn_len == 0) { asn_error(b, "no identifier for header"); return (ASN_ERR_EOBUF); } *type = *b->asn_cptr; if ((*type & ASN_TYPE_MASK) > 0x30) { asn_error(b, "types > 0x30 not supported (%u)", *type & ASN_TYPE_MASK); return (ASN_ERR_FAILED); } b->asn_cptr++; b->asn_len--; if (b->asn_len == 0) { asn_error(b, "no length field"); return (ASN_ERR_EOBUF); } if (*b->asn_cptr & 0x80) { length = *b->asn_cptr++ & 0x7f; b->asn_len--; if (length == 0) { asn_error(b, "indefinite length not supported"); return (ASN_ERR_FAILED); } if (length > ASN_MAXLENLEN) { asn_error(b, "long length too long (%u)", length); return (ASN_ERR_FAILED); } if (length > b->asn_len) { asn_error(b, "long length truncated"); return (ASN_ERR_EOBUF); } *len = 0; while (length--) { *len = (*len << 8) | *b->asn_cptr++; b->asn_len--; } } else { *len = *b->asn_cptr++; b->asn_len--; } return (ASN_ERR_OK); } /* * Write a length field (restricted to values < 2^32-1) and return the * number of bytes this field takes. If ptr is NULL, the length is computed * but nothing is written. If the length would be too large return 0. */ static u_int asn_put_len(u_char *ptr, asn_len_t len) { u_int lenlen, lenlen1; asn_len_t tmp; if (len > ASN_MAXLEN) { asn_error(NULL, "encoding length too long: (%u)", len); return (0); } if (len <= 127) { if (ptr) *ptr++ = (u_char)len; return (1); } else { lenlen = 0; /* compute number of bytes for value (is at least 1) */ for (tmp = len; tmp != 0; tmp >>= 8) lenlen++; if (ptr != NULL) { *ptr++ = (u_char)lenlen | 0x80; lenlen1 = lenlen; while (lenlen1-- > 0) { ptr[lenlen1] = len & 0xff; len >>= 8; } } return (lenlen + 1); } } /* * Write a header (tag and length fields). * Tags are restricted to one byte tags (value <= 0x30) and the * lenght field to 16-bit. All errors stop the encoding. */ enum asn_err asn_put_header(struct asn_buf *b, u_char type, asn_len_t len) { u_int lenlen; /* tag field */ if ((type & ASN_TYPE_MASK) > 0x30) { asn_error(NULL, "types > 0x30 not supported (%u)", type & ASN_TYPE_MASK); return (ASN_ERR_FAILED); } if (b->asn_len == 0) return (ASN_ERR_EOBUF); *b->asn_ptr++ = type; b->asn_len--; /* length field */ if ((lenlen = asn_put_len(NULL, len)) == 0) return (ASN_ERR_FAILED); if (b->asn_len < lenlen) return (ASN_ERR_EOBUF); (void)asn_put_len(b->asn_ptr, len); b->asn_ptr += lenlen; b->asn_len -= lenlen; return (ASN_ERR_OK); } /* * This constructs a temporary sequence header with space for the maximum * length field (three byte). Set the pointer that ptr points to to the * start of the encoded header. This is used for a later call to * asn_commit_header which will fix-up the length field and move the * value if needed. All errors should stop the encoding. */ #define TEMP_LEN (1 + ASN_MAXLENLEN + 1) enum asn_err asn_put_temp_header(struct asn_buf *b, u_char type, u_char **ptr) { int ret; if (b->asn_len < TEMP_LEN) return (ASN_ERR_EOBUF); *ptr = b->asn_ptr; if ((ret = asn_put_header(b, type, ASN_MAXLEN)) == ASN_ERR_OK) assert(b->asn_ptr == *ptr + TEMP_LEN); return (ret); } enum asn_err asn_commit_header(struct asn_buf *b, u_char *ptr, size_t *moved) { asn_len_t len; u_int lenlen, shift; /* compute length of encoded value without header */ len = b->asn_ptr - (ptr + TEMP_LEN); /* insert length. may not fail. */ lenlen = asn_put_len(ptr + 1, len); if (lenlen > TEMP_LEN - 1) return (ASN_ERR_FAILED); if (lenlen < TEMP_LEN - 1) { /* shift value down */ shift = (TEMP_LEN - 1) - lenlen; memmove(ptr + 1 + lenlen, ptr + TEMP_LEN, len); b->asn_ptr -= shift; b->asn_len += shift; if (moved != NULL) *moved = shift; } return (ASN_ERR_OK); } #undef TEMP_LEN /* * BER integer. This may be used to get a signed 64 bit integer at maximum. * The maximum length should be checked by the caller. This cannot overflow * if the caller ensures that len is at maximum 8. * * */ static enum asn_err asn_get_real_integer(struct asn_buf *b, asn_len_t len, int64_t *vp) { uint64_t val; int neg = 0; enum asn_err err; if (b->asn_len < len) { asn_error(b, "truncated integer"); return (ASN_ERR_EOBUF); } if (len == 0) { asn_error(b, "zero-length integer"); *vp = 0; return (ASN_ERR_BADLEN); } err = ASN_ERR_OK; if (len > 8) err = ASN_ERR_RANGE; else if (len > 1 && ((*b->asn_cptr == 0x00 && (b->asn_cptr[1] & 0x80) == 0) || (*b->asn_cptr == 0xff && (b->asn_cptr[1] & 0x80) == 0x80))) { asn_error(b, "non-minimal integer"); err = ASN_ERR_BADLEN; } if (*b->asn_cptr & 0x80) neg = 1; val = 0; while (len--) { val <<= 8; val |= neg ? (u_char)~*b->asn_cptr : *b->asn_cptr; b->asn_len--; b->asn_cptr++; } if (neg) { *vp = -(int64_t)val - 1; } else *vp = (int64_t)val; return (err); } /* * Write a signed integer with the given type. The caller has to ensure * that the actual value is ok for this type. */ static enum asn_err asn_put_real_integer(struct asn_buf *b, u_char type, int64_t ival) { int i, neg = 0; # define OCTETS 8 u_char buf[OCTETS]; uint64_t val; enum asn_err ret; if (ival < 0) { /* this may fail if |INT64_MIN| > |INT64_MAX| and * the value is between * INT64_MIN <= ival < -(INT64_MAX+1) */ val = (uint64_t)-(ival + 1); neg = 1; } else val = (uint64_t)ival; /* split the value into octets */ for (i = OCTETS - 1; i >= 0; i--) { buf[i] = val & 0xff; if (neg) buf[i] = ~buf[i]; val >>= 8; } /* no leading 9 zeroes or ones */ for (i = 0; i < OCTETS - 1; i++) if (!((buf[i] == 0xff && (buf[i + 1] & 0x80) != 0) || (buf[i] == 0x00 && (buf[i + 1] & 0x80) == 0))) break; if ((ret = asn_put_header(b, type, OCTETS - i))) return (ret); if (OCTETS - (u_int)i > b->asn_len) return (ASN_ERR_EOBUF); while (i < OCTETS) { *b->asn_ptr++ = buf[i++]; b->asn_len--; } return (ASN_ERR_OK); # undef OCTETS } /* * The same for unsigned 64-bitters. Here we have the problem, that overflow * can happen, because the value maybe 9 bytes long. In this case the * first byte must be 0. */ static enum asn_err asn_get_real_unsigned(struct asn_buf *b, asn_len_t len, uint64_t *vp) { enum asn_err err; if (b->asn_len < len) { asn_error(b, "truncated integer"); return (ASN_ERR_EOBUF); } if (len == 0) { asn_error(b, "zero-length integer"); *vp = 0; return (ASN_ERR_BADLEN); } err = ASN_ERR_OK; *vp = 0; if ((*b->asn_cptr & 0x80) || (len == 9 && *b->asn_cptr != 0)) { /* negative integer or too larger */ *vp = 0xffffffffffffffffULL; err = ASN_ERR_RANGE; } else if (len > 1 && *b->asn_cptr == 0x00 && (b->asn_cptr[1] & 0x80) == 0) { asn_error(b, "non-minimal unsigned"); err = ASN_ERR_BADLEN; } while (len--) { *vp = (*vp << 8) | *b->asn_cptr++; b->asn_len--; } return (err); } /* * Values with the msb on need 9 octets. */ static int asn_put_real_unsigned(struct asn_buf *b, u_char type, uint64_t val) { int i; # define OCTETS 9 u_char buf[OCTETS]; enum asn_err ret; /* split the value into octets */ for (i = OCTETS - 1; i >= 0; i--) { buf[i] = val & 0xff; val >>= 8; } /* no leading 9 zeroes */ for (i = 0; i < OCTETS - 1; i++) if (!(buf[i] == 0x00 && (buf[i + 1] & 0x80) == 0)) break; if ((ret = asn_put_header(b, type, OCTETS - i))) return (ret); if (OCTETS - (u_int)i > b->asn_len) return (ASN_ERR_EOBUF); while (i < OCTETS) { *b->asn_ptr++ = buf[i++]; b->asn_len--; } #undef OCTETS return (ASN_ERR_OK); } /* * The ASN.1 INTEGER type is restricted to 32-bit signed by the SMI. */ enum asn_err asn_get_integer_raw(struct asn_buf *b, asn_len_t len, int32_t *vp) { int64_t val; enum asn_err ret; if ((ret = asn_get_real_integer(b, len, &val)) == ASN_ERR_OK) { if (len > 4) ret = ASN_ERR_BADLEN; else if (val > INT32_MAX || val < INT32_MIN) /* may not happen */ ret = ASN_ERR_RANGE; *vp = (int32_t)val; } return (ret); } enum asn_err asn_get_integer(struct asn_buf *b, int32_t *vp) { asn_len_t len; u_char type; enum asn_err err; if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) return (err); if (type != ASN_TYPE_INTEGER) { asn_error(b, "bad type for integer (%u)", type); return (ASN_ERR_TAG); } return (asn_get_integer_raw(b, len, vp)); } enum asn_err asn_put_integer(struct asn_buf *b, int32_t val) { return (asn_put_real_integer(b, ASN_TYPE_INTEGER, val)); } /* * OCTETSTRING * * <0x04> * * Get an octetstring. noctets must point to the buffer size and on * return will contain the size of the octetstring, regardless of the * buffer size. */ enum asn_err asn_get_octetstring_raw(struct asn_buf *b, asn_len_t len, u_char *octets, u_int *noctets) { enum asn_err err = ASN_ERR_OK; if (*noctets < len) { asn_error(b, "octetstring truncated"); err = ASN_ERR_RANGE; } if (b->asn_len < len) { asn_error(b, "truncatet octetstring"); return (ASN_ERR_EOBUF); } if (*noctets < len) memcpy(octets, b->asn_cptr, *noctets); else memcpy(octets, b->asn_cptr, len); *noctets = len; b->asn_cptr += len; b->asn_len -= len; return (err); } enum asn_err asn_get_octetstring(struct asn_buf *b, u_char *octets, u_int *noctets) { enum asn_err err; u_char type; asn_len_t len; if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) return (err); if (type != ASN_TYPE_OCTETSTRING) { asn_error(b, "bad type for octetstring (%u)", type); return (ASN_ERR_TAG); } return (asn_get_octetstring_raw(b, len, octets, noctets)); } enum asn_err asn_put_octetstring(struct asn_buf *b, const u_char *octets, u_int noctets) { enum asn_err ret; if ((ret = asn_put_header(b, ASN_TYPE_OCTETSTRING, noctets)) != ASN_ERR_OK) return (ret); if (b->asn_len < noctets) return (ASN_ERR_EOBUF); memcpy(b->asn_ptr, octets, noctets); b->asn_ptr += noctets; b->asn_len -= noctets; return (ASN_ERR_OK); } /* * NULL * * <0x05> <0x00> */ enum asn_err asn_get_null_raw(struct asn_buf *b, asn_len_t len) { if (len != 0) { if (b->asn_len < len) { asn_error(b, "truncated NULL"); return (ASN_ERR_EOBUF); } asn_error(b, "bad length for NULL (%u)", len); b->asn_len -= len; b->asn_ptr += len; return (ASN_ERR_BADLEN); } return (ASN_ERR_OK); } enum asn_err asn_get_null(struct asn_buf *b) { u_char type; asn_len_t len; enum asn_err err; if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) return (err); if (type != ASN_TYPE_NULL) { asn_error(b, "bad type for NULL (%u)", type); return (ASN_ERR_TAG); } return (asn_get_null_raw(b, len)); } enum asn_err asn_put_null(struct asn_buf *b) { return (asn_put_header(b, ASN_TYPE_NULL, 0)); } enum asn_err asn_put_exception(struct asn_buf *b, u_int except) { return (asn_put_header(b, ASN_CLASS_CONTEXT | except, 0)); } /* * OBJID * * <0x06> */ enum asn_err asn_get_objid_raw(struct asn_buf *b, asn_len_t len, struct asn_oid *oid) { asn_subid_t subid; enum asn_err err; if (b->asn_len < len) { asn_error(b, "truncated OBJID"); return (ASN_ERR_EOBUF); } oid->len = 0; if (len == 0) { asn_error(b, "short OBJID"); oid->subs[oid->len++] = 0; oid->subs[oid->len++] = 0; return (ASN_ERR_BADLEN); } err = ASN_ERR_OK; while (len != 0) { if (oid->len == ASN_MAXOIDLEN) { asn_error(b, "OID too long (%u)", oid->len); b->asn_cptr += len; b->asn_len -= len; return (ASN_ERR_BADLEN); } subid = 0; do { if (len == 0) { asn_error(b, "unterminated subid"); return (ASN_ERR_EOBUF); } if (subid > (ASN_MAXID >> 7)) { asn_error(b, "OBID subid too larger"); err = ASN_ERR_RANGE; } subid = (subid << 7) | (*b->asn_cptr & 0x7f); len--; b->asn_len--; } while (*b->asn_cptr++ & 0x80); if (oid->len == 0) { if (subid < 80) { oid->subs[oid->len++] = subid / 40; oid->subs[oid->len++] = subid % 40; } else { oid->subs[oid->len++] = 2; oid->subs[oid->len++] = subid - 80; } } else { oid->subs[oid->len++] = subid; } } return (err); } enum asn_err asn_get_objid(struct asn_buf *b, struct asn_oid *oid) { u_char type; asn_len_t len; enum asn_err err; if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) return (err); if (type != ASN_TYPE_OBJID) { asn_error(b, "bad type for OBJID (%u)", type); return (ASN_ERR_TAG); } return (asn_get_objid_raw(b, len, oid)); } enum asn_err asn_put_objid(struct asn_buf *b, const struct asn_oid *oid) { asn_subid_t first, sub; enum asn_err err, err1; u_int i, oidlen; asn_len_t len; err = ASN_ERR_OK; if (oid->len == 0) { /* illegal */ asn_error(NULL, "short oid"); err = ASN_ERR_RANGE; first = 0; oidlen = 2; } else if (oid->len == 1) { /* illegal */ asn_error(b, "short oid"); if (oid->subs[0] > 2) asn_error(NULL, "oid[0] too large (%u)", oid->subs[0]); err = ASN_ERR_RANGE; first = oid->subs[0] * 40; oidlen = 2; } else { if (oid->len > ASN_MAXOIDLEN) { asn_error(NULL, "oid too long %u", oid->len); err = ASN_ERR_RANGE; } if (oid->subs[0] > 2 || - (oid->subs[0] < 2 && oid->subs[0] >= 40)) { + (oid->subs[0] < 2 && oid->subs[1] >= 40)) { asn_error(NULL, "oid out of range (%u,%u)", oid->subs[0], oid->subs[1]); err = ASN_ERR_RANGE; } first = 40 * oid->subs[0] + oid->subs[1]; oidlen = oid->len; } len = 0; for (i = 1; i < oidlen; i++) { sub = (i == 1) ? first : oid->subs[i]; if (sub > ASN_MAXID) { asn_error(NULL, "oid subid too large"); err = ASN_ERR_RANGE; } len += (sub <= 0x7f) ? 1 : (sub <= 0x3fff) ? 2 : (sub <= 0x1fffff) ? 3 : (sub <= 0xfffffff) ? 4 : 5; } if ((err1 = asn_put_header(b, ASN_TYPE_OBJID, len)) != ASN_ERR_OK) return (err1); if (b->asn_len < len) return (ASN_ERR_EOBUF); for (i = 1; i < oidlen; i++) { sub = (i == 1) ? first : oid->subs[i]; if (sub <= 0x7f) { *b->asn_ptr++ = sub; b->asn_len--; } else if (sub <= 0x3fff) { *b->asn_ptr++ = (sub >> 7) | 0x80; *b->asn_ptr++ = sub & 0x7f; b->asn_len -= 2; } else if (sub <= 0x1fffff) { *b->asn_ptr++ = (sub >> 14) | 0x80; *b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80; *b->asn_ptr++ = sub & 0x7f; b->asn_len -= 3; } else if (sub <= 0xfffffff) { *b->asn_ptr++ = (sub >> 21) | 0x80; *b->asn_ptr++ = ((sub >> 14) & 0x7f) | 0x80; *b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80; *b->asn_ptr++ = sub & 0x7f; b->asn_len -= 4; } else { *b->asn_ptr++ = (sub >> 28) | 0x80; *b->asn_ptr++ = ((sub >> 21) & 0x7f) | 0x80; *b->asn_ptr++ = ((sub >> 14) & 0x7f) | 0x80; *b->asn_ptr++ = ((sub >> 7) & 0x7f) | 0x80; *b->asn_ptr++ = sub & 0x7f; b->asn_len -= 5; } } return (err); } /* * SEQUENCE header * * <0x10|0x20> */ enum asn_err asn_get_sequence(struct asn_buf *b, asn_len_t *len) { u_char type; enum asn_err err; if ((err = asn_get_header(b, &type, len)) != ASN_ERR_OK) return (err); if (type != (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED)) { asn_error(b, "bad sequence type %u", type); return (ASN_ERR_TAG); } if (*len > b->asn_len) { asn_error(b, "truncated sequence"); return (ASN_ERR_EOBUF); } return (ASN_ERR_OK); } /* * Application types * * 0x40 4 MSB 2MSB 2LSB LSB */ enum asn_err asn_get_ipaddress_raw(struct asn_buf *b, asn_len_t len, u_char *addr) { u_int i; if (b->asn_len < len) { asn_error(b, "truncated ip-address"); return (ASN_ERR_EOBUF); } if (len < 4) { asn_error(b, "short length for ip-Address %u", len); for (i = 0; i < len; i++) *addr++ = *b->asn_cptr++; while (i++ < len) *addr++ = 0; b->asn_len -= len; return (ASN_ERR_BADLEN); } for (i = 0; i < 4; i++) *addr++ = *b->asn_cptr++; b->asn_cptr += len - 4; b->asn_len -= len; return (ASN_ERR_OK); } enum asn_err asn_get_ipaddress(struct asn_buf *b, u_char *addr) { u_char type; asn_len_t len; enum asn_err err; if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) return (err); if (type != (ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS)) { asn_error(b, "bad type for ip-address %u", type); return (ASN_ERR_TAG); } return (asn_get_ipaddress_raw(b, len, addr)); } enum asn_err asn_put_ipaddress(struct asn_buf *b, const u_char *addr) { enum asn_err err; if ((err = asn_put_header(b, ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS, 4)) != ASN_ERR_OK) return (err); if (b->asn_len < 4) return (ASN_ERR_EOBUF); memcpy(b->asn_ptr, addr, 4); b->asn_ptr += 4; b->asn_len -= 4; return (ASN_ERR_OK); } /* * UNSIGNED32 * * 0x42|0x41 ... */ enum asn_err asn_get_uint32_raw(struct asn_buf *b, asn_len_t len, uint32_t *vp) { uint64_t v; enum asn_err err; if ((err = asn_get_real_unsigned(b, len, &v)) == ASN_ERR_OK) { if (len > 5) { asn_error(b, "uint32 too long %u", len); err = ASN_ERR_BADLEN; } else if (v > UINT32_MAX) { asn_error(b, "uint32 too large %llu", v); err = ASN_ERR_RANGE; } *vp = (uint32_t)v; } return (err); } enum asn_err asn_put_uint32(struct asn_buf *b, u_char type, uint32_t val) { uint64_t v = val; return (asn_put_real_unsigned(b, ASN_CLASS_APPLICATION|type, v)); } /* * COUNTER64 * 0x46 ... */ enum asn_err asn_get_counter64_raw(struct asn_buf *b, asn_len_t len, uint64_t *vp) { return (asn_get_real_unsigned(b, len, vp)); } enum asn_err asn_put_counter64(struct asn_buf *b, uint64_t val) { return (asn_put_real_unsigned(b, ASN_CLASS_APPLICATION | ASN_APP_COUNTER64, val)); } /* * TimeTicks * 0x43 ... */ enum asn_err asn_get_timeticks(struct asn_buf *b, uint32_t *vp) { asn_len_t len; u_char type; enum asn_err err; if ((err = asn_get_header(b, &type, &len)) != ASN_ERR_OK) return (err); if (type != (ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS)) { asn_error(b, "bad type for timeticks %u", type); return (ASN_ERR_TAG); } return (asn_get_uint32_raw(b, len, vp)); } enum asn_err asn_put_timeticks(struct asn_buf *b, uint32_t val) { uint64_t v = val; return (asn_put_real_unsigned(b, ASN_CLASS_APPLICATION | ASN_APP_TIMETICKS, v)); } /* * Construct a new OID by taking a range of sub ids of the original oid. */ void asn_slice_oid(struct asn_oid *dest, const struct asn_oid *src, u_int from, u_int to) { if (from >= to) { dest->len = 0; return; } dest->len = to - from; memcpy(dest->subs, &src->subs[from], dest->len * sizeof(dest->subs[0])); } /* * Append from to to */ void asn_append_oid(struct asn_oid *to, const struct asn_oid *from) { memcpy(&to->subs[to->len], &from->subs[0], from->len * sizeof(from->subs[0])); to->len += from->len; } /* * Skip a value */ enum asn_err asn_skip(struct asn_buf *b, asn_len_t len) { if (b->asn_len < len) return (ASN_ERR_EOBUF); b->asn_cptr += len; b->asn_len -= len; return (ASN_ERR_OK); } /* * Add a padding */ enum asn_err asn_pad(struct asn_buf *b, asn_len_t len) { if (b->asn_len < len) return (ASN_ERR_EOBUF); b->asn_ptr += len; b->asn_len -= len; return (ASN_ERR_OK); } /* * Compare two OIDs. * * o1 < o2 : -1 * o1 > o2 : +1 * o1 = o2 : 0 */ int asn_compare_oid(const struct asn_oid *o1, const struct asn_oid *o2) { u_long i; for (i = 0; i < o1->len && i < o2->len; i++) { if (o1->subs[i] < o2->subs[i]) return (-1); if (o1->subs[i] > o2->subs[i]) return (+1); } if (o1->len < o2->len) return (-1); if (o1->len > o2->len) return (+1); return (0); } /* * Check whether an OID is a sub-string of another OID. */ int asn_is_suboid(const struct asn_oid *o1, const struct asn_oid *o2) { u_long i; for (i = 0; i < o1->len; i++) if (i >= o2->len || o1->subs[i] != o2->subs[i]) return (0); return (1); } /* * Put a string representation of an oid into a user buffer. This buffer * is assumed to be at least ASN_OIDSTRLEN characters long. * * sprintf is assumed not to fail here. */ char * asn_oid2str_r(const struct asn_oid *oid, char *buf) { u_int len, i; char *ptr; if ((len = oid->len) > ASN_MAXOIDLEN) len = ASN_MAXOIDLEN; buf[0] = '\0'; for (i = 0, ptr = buf; i < len; i++) { if (i > 0) *ptr++ = '.'; ptr += sprintf(ptr, "%u", oid->subs[i]); } return (buf); } /* * Make a string from an OID in a private buffer. */ char * asn_oid2str(const struct asn_oid *oid) { static char str[ASN_OIDSTRLEN]; return (asn_oid2str_r(oid, str)); } static void asn_error_func(const struct asn_buf *b, const char *err, ...) { va_list ap; u_long i; fprintf(stderr, "ASN.1: "); va_start(ap, err); vfprintf(stderr, err, ap); va_end(ap); if (b != NULL) { fprintf(stderr, " at"); for (i = 0; b->asn_len > i; i++) fprintf(stderr, " %02x", b->asn_cptr[i]); } fprintf(stderr, "\n"); } Index: stable/9/contrib/bsnmp/lib/snmp.c =================================================================== --- stable/9/contrib/bsnmp/lib/snmp.c (revision 301661) +++ stable/9/contrib/bsnmp/lib/snmp.c (revision 301662) @@ -1,1453 +1,1455 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/lib/snmp.c,v 1.40 2005/10/04 14:32:42 brandt_h Exp $ * * SNMP */ #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmppriv.h" static void snmp_error_func(const char *, ...); static void snmp_printf_func(const char *, ...); void (*snmp_error)(const char *, ...) = snmp_error_func; void (*snmp_printf)(const char *, ...) = snmp_printf_func; /* * Get the next variable binding from the list. * ASN errors on the sequence or the OID are always fatal. */ static enum asn_err get_var_binding(struct asn_buf *b, struct snmp_value *binding) { u_char type; asn_len_t len, trailer; enum asn_err err; if (asn_get_sequence(b, &len) != ASN_ERR_OK) { snmp_error("cannot parse varbind header"); return (ASN_ERR_FAILED); } /* temporary truncate the length so that the parser does not * eat up bytes behind the sequence in the case the encoding is * wrong of inner elements. */ trailer = b->asn_len - len; b->asn_len = len; if (asn_get_objid(b, &binding->var) != ASN_ERR_OK) { snmp_error("cannot parse binding objid"); return (ASN_ERR_FAILED); } if (asn_get_header(b, &type, &len) != ASN_ERR_OK) { snmp_error("cannot parse binding value header"); return (ASN_ERR_FAILED); } switch (type) { case ASN_TYPE_NULL: binding->syntax = SNMP_SYNTAX_NULL; err = asn_get_null_raw(b, len); break; case ASN_TYPE_INTEGER: binding->syntax = SNMP_SYNTAX_INTEGER; err = asn_get_integer_raw(b, len, &binding->v.integer); break; case ASN_TYPE_OCTETSTRING: binding->syntax = SNMP_SYNTAX_OCTETSTRING; binding->v.octetstring.octets = malloc(len); if (binding->v.octetstring.octets == NULL) { snmp_error("%s", strerror(errno)); return (ASN_ERR_FAILED); } binding->v.octetstring.len = len; err = asn_get_octetstring_raw(b, len, binding->v.octetstring.octets, &binding->v.octetstring.len); if (ASN_ERR_STOPPED(err)) { free(binding->v.octetstring.octets); binding->v.octetstring.octets = NULL; } break; case ASN_TYPE_OBJID: binding->syntax = SNMP_SYNTAX_OID; err = asn_get_objid_raw(b, len, &binding->v.oid); break; case ASN_CLASS_APPLICATION|ASN_APP_IPADDRESS: binding->syntax = SNMP_SYNTAX_IPADDRESS; err = asn_get_ipaddress_raw(b, len, binding->v.ipaddress); break; case ASN_CLASS_APPLICATION|ASN_APP_TIMETICKS: binding->syntax = SNMP_SYNTAX_TIMETICKS; err = asn_get_uint32_raw(b, len, &binding->v.uint32); break; case ASN_CLASS_APPLICATION|ASN_APP_COUNTER: binding->syntax = SNMP_SYNTAX_COUNTER; err = asn_get_uint32_raw(b, len, &binding->v.uint32); break; case ASN_CLASS_APPLICATION|ASN_APP_GAUGE: binding->syntax = SNMP_SYNTAX_GAUGE; err = asn_get_uint32_raw(b, len, &binding->v.uint32); break; case ASN_CLASS_APPLICATION|ASN_APP_COUNTER64: binding->syntax = SNMP_SYNTAX_COUNTER64; err = asn_get_counter64_raw(b, len, &binding->v.counter64); break; case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHOBJECT: binding->syntax = SNMP_SYNTAX_NOSUCHOBJECT; err = asn_get_null_raw(b, len); break; case ASN_CLASS_CONTEXT | ASN_EXCEPT_NOSUCHINSTANCE: binding->syntax = SNMP_SYNTAX_NOSUCHINSTANCE; err = asn_get_null_raw(b, len); break; case ASN_CLASS_CONTEXT | ASN_EXCEPT_ENDOFMIBVIEW: binding->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; err = asn_get_null_raw(b, len); break; default: if ((err = asn_skip(b, len)) == ASN_ERR_OK) err = ASN_ERR_TAG; snmp_error("bad binding value type 0x%x", type); break; } if (ASN_ERR_STOPPED(err)) { snmp_error("cannot parse binding value"); return (err); } if (b->asn_len != 0) snmp_error("ignoring junk at end of binding"); b->asn_len = trailer; return (err); } /* * Parse the different PDUs contents. Any ASN error in the outer components * are fatal. Only errors in variable values may be tolerated. If all * components can be parsed it returns either ASN_ERR_OK or the first * error that was found. */ enum asn_err snmp_parse_pdus_hdr(struct asn_buf *b, struct snmp_pdu *pdu, asn_len_t *lenp) { if (pdu->type == SNMP_PDU_TRAP) { if (asn_get_objid(b, &pdu->enterprise) != ASN_ERR_OK) { snmp_error("cannot parse trap enterprise"); return (ASN_ERR_FAILED); } if (asn_get_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK) { snmp_error("cannot parse trap agent address"); return (ASN_ERR_FAILED); } if (asn_get_integer(b, &pdu->generic_trap) != ASN_ERR_OK) { snmp_error("cannot parse 'generic-trap'"); return (ASN_ERR_FAILED); } if (asn_get_integer(b, &pdu->specific_trap) != ASN_ERR_OK) { snmp_error("cannot parse 'specific-trap'"); return (ASN_ERR_FAILED); } if (asn_get_timeticks(b, &pdu->time_stamp) != ASN_ERR_OK) { snmp_error("cannot parse trap 'time-stamp'"); return (ASN_ERR_FAILED); } } else { if (asn_get_integer(b, &pdu->request_id) != ASN_ERR_OK) { snmp_error("cannot parse 'request-id'"); return (ASN_ERR_FAILED); } if (asn_get_integer(b, &pdu->error_status) != ASN_ERR_OK) { snmp_error("cannot parse 'error_status'"); return (ASN_ERR_FAILED); } if (asn_get_integer(b, &pdu->error_index) != ASN_ERR_OK) { snmp_error("cannot parse 'error_index'"); return (ASN_ERR_FAILED); } } if (asn_get_sequence(b, lenp) != ASN_ERR_OK) { snmp_error("cannot get varlist header"); return (ASN_ERR_FAILED); } return (ASN_ERR_OK); } static enum asn_err parse_pdus(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) { asn_len_t len, trailer; struct snmp_value *v; enum asn_err err, err1; err = snmp_parse_pdus_hdr(b, pdu, &len); if (ASN_ERR_STOPPED(err)) return (err); trailer = b->asn_len - len; v = pdu->bindings; err = ASN_ERR_OK; while (b->asn_len != 0) { if (pdu->nbindings == SNMP_MAX_BINDINGS) { snmp_error("too many bindings (> %u) in PDU", SNMP_MAX_BINDINGS); return (ASN_ERR_FAILED); } err1 = get_var_binding(b, v); if (ASN_ERR_STOPPED(err1)) return (ASN_ERR_FAILED); if (err1 != ASN_ERR_OK && err == ASN_ERR_OK) { err = err1; *ip = pdu->nbindings + 1; } pdu->nbindings++; v++; } b->asn_len = trailer; return (err); } static enum asn_err parse_secparams(struct asn_buf *b, struct snmp_pdu *pdu) { asn_len_t octs_len; u_char buf[256]; /* XXX: calc max possible size here */ struct asn_buf tb; memset(buf, 0, 256); tb.asn_ptr = buf; tb.asn_len = 256; + u_int len; - if (asn_get_octetstring(b, buf, &tb.asn_len) != ASN_ERR_OK) { + if (asn_get_octetstring(b, buf, &len) != ASN_ERR_OK) { snmp_error("cannot parse usm header"); return (ASN_ERR_FAILED); } + tb.asn_len = len; if (asn_get_sequence(&tb, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode usm header"); return (ASN_ERR_FAILED); } octs_len = SNMP_ENGINE_ID_SIZ; if (asn_get_octetstring(&tb, (u_char *)&pdu->engine.engine_id, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode msg engine id"); return (ASN_ERR_FAILED); } pdu->engine.engine_len = octs_len; if (asn_get_integer(&tb, &pdu->engine.engine_boots) != ASN_ERR_OK) { snmp_error("cannot decode msg engine boots"); return (ASN_ERR_FAILED); } if (asn_get_integer(&tb, &pdu->engine.engine_time) != ASN_ERR_OK) { snmp_error("cannot decode msg engine time"); return (ASN_ERR_FAILED); } octs_len = SNMP_ADM_STR32_SIZ - 1; if (asn_get_octetstring(&tb, (u_char *)&pdu->user.sec_name, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode msg user name"); return (ASN_ERR_FAILED); } pdu->user.sec_name[octs_len] = '\0'; octs_len = sizeof(pdu->msg_digest); if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_digest, &octs_len) != ASN_ERR_OK || ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0 && octs_len != sizeof(pdu->msg_digest))) { snmp_error("cannot decode msg authentication param"); return (ASN_ERR_FAILED); } octs_len = sizeof(pdu->msg_salt); if (asn_get_octetstring(&tb, (u_char *)&pdu->msg_salt, &octs_len) != ASN_ERR_OK ||((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && octs_len != sizeof(pdu->msg_salt))) { snmp_error("cannot decode msg authentication param"); return (ASN_ERR_FAILED); } if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { pdu->digest_ptr = b->asn_ptr - SNMP_USM_AUTH_SIZE; pdu->digest_ptr -= octs_len + ASN_MAXLENLEN; } return (ASN_ERR_OK); } static enum snmp_code pdu_encode_secparams(struct asn_buf *b, struct snmp_pdu *pdu) { u_char buf[256], *sptr; struct asn_buf tb; size_t auth_off, moved = 0; auth_off = 0; memset(buf, 0, 256); tb.asn_ptr = buf; tb.asn_len = 256; if (asn_put_temp_header(&tb, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), &sptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_octetstring(&tb, (u_char *)pdu->engine.engine_id, pdu->engine.engine_len) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(&tb, pdu->engine.engine_boots) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(&tb, pdu->engine.engine_time) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_octetstring(&tb, (u_char *)pdu->user.sec_name, strlen(pdu->user.sec_name)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) { auth_off = sizeof(buf) - tb.asn_len + ASN_MAXLENLEN; if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest, sizeof(pdu->msg_digest)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { if (asn_put_octetstring(&tb, (u_char *)pdu->msg_digest, 0) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0) { if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt, sizeof(pdu->msg_salt)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { if (asn_put_octetstring(&tb, (u_char *)pdu->msg_salt, 0) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if (asn_commit_header(&tb, sptr, &moved) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) pdu->digest_ptr = b->asn_ptr + auth_off - moved; if (asn_put_octetstring(b, buf, sizeof(buf) - tb.asn_len) != ASN_ERR_OK) return (SNMP_CODE_FAILED); pdu->digest_ptr += ASN_MAXLENLEN; if ((pdu->flags & SNMP_MSG_PRIV_FLAG) != 0 && asn_put_temp_header(b, ASN_TYPE_OCTETSTRING, &pdu->encrypted_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); return (SNMP_CODE_OK); } /* * Decode the PDU except for the variable bindings itself. * If decoding fails because of a bad binding, but the rest can be * decoded, ip points to the index of the failed variable (errors * OORANGE, BADLEN or BADVERS). */ enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) { enum snmp_code code; if ((code = snmp_pdu_decode_header(b, pdu)) != SNMP_CODE_OK) return (code); if (pdu->version == SNMP_V3) { if (pdu->security_model != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); if ((code = snmp_pdu_decode_secmode(b, pdu)) != SNMP_CODE_OK) return (code); } code = snmp_pdu_decode_scoped(b, pdu, ip); switch (code) { case SNMP_CODE_FAILED: snmp_pdu_free(pdu); break; case SNMP_CODE_BADENC: if (pdu->version == SNMP_Verr) return (SNMP_CODE_BADVERS); default: break; } return (code); } enum snmp_code snmp_pdu_decode_header(struct asn_buf *b, struct snmp_pdu *pdu) { int32_t version; u_int octs_len; asn_len_t len; pdu->outer_ptr = b->asn_ptr; pdu->outer_len = b->asn_len; if (asn_get_sequence(b, &len) != ASN_ERR_OK) { snmp_error("cannot decode pdu header"); return (SNMP_CODE_FAILED); } if (b->asn_len < len) { snmp_error("outer sequence value too short"); return (SNMP_CODE_FAILED); } if (b->asn_len != len) { snmp_error("ignoring trailing junk in message"); b->asn_len = len; } if (asn_get_integer(b, &version) != ASN_ERR_OK) { snmp_error("cannot decode version"); return (SNMP_CODE_FAILED); } if (version == 0) pdu->version = SNMP_V1; else if (version == 1) pdu->version = SNMP_V2c; else if (version == 3) pdu->version = SNMP_V3; else { pdu->version = SNMP_Verr; snmp_error("unsupported SNMP version"); return (SNMP_CODE_BADENC); } if (pdu->version == SNMP_V3) { if (asn_get_sequence(b, &len) != ASN_ERR_OK) { snmp_error("cannot decode pdu global data header"); return (SNMP_CODE_FAILED); } if (asn_get_integer(b, &pdu->identifier) != ASN_ERR_OK) { snmp_error("cannot decode msg indetifier"); return (SNMP_CODE_FAILED); } if (asn_get_integer(b, &pdu->engine.max_msg_size) != ASN_ERR_OK) { snmp_error("cannot decode msg size"); return (SNMP_CODE_FAILED); } octs_len = 1; if (asn_get_octetstring(b, (u_char *)&pdu->flags, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode msg flags"); return (SNMP_CODE_FAILED); } if (asn_get_integer(b, &pdu->security_model) != ASN_ERR_OK) { snmp_error("cannot decode msg size"); return (SNMP_CODE_FAILED); } if (pdu->security_model != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); if (parse_secparams(b, pdu) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { octs_len = SNMP_COMMUNITY_MAXLEN; if (asn_get_octetstring(b, (u_char *)pdu->community, &octs_len) != ASN_ERR_OK) { snmp_error("cannot decode community"); return (SNMP_CODE_FAILED); } pdu->community[octs_len] = '\0'; } return (SNMP_CODE_OK); } enum snmp_code snmp_pdu_decode_scoped(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *ip) { u_char type; asn_len_t len, trailer; enum asn_err err; if (pdu->version == SNMP_V3) { if (asn_get_sequence(b, &len) != ASN_ERR_OK) { snmp_error("cannot decode scoped pdu header"); return (SNMP_CODE_FAILED); } len = SNMP_ENGINE_ID_SIZ; if (asn_get_octetstring(b, (u_char *)&pdu->context_engine, &len) != ASN_ERR_OK) { snmp_error("cannot decode msg context engine"); return (SNMP_CODE_FAILED); } pdu->context_engine_len = len; len = SNMP_CONTEXT_NAME_SIZ; if (asn_get_octetstring(b, (u_char *)&pdu->context_name, &len) != ASN_ERR_OK) { snmp_error("cannot decode msg context name"); return (SNMP_CODE_FAILED); } pdu->context_name[len] = '\0'; } if (asn_get_header(b, &type, &len) != ASN_ERR_OK) { snmp_error("cannot get pdu header"); return (SNMP_CODE_FAILED); } if ((type & ~ASN_TYPE_MASK) != (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { snmp_error("bad pdu header tag"); return (SNMP_CODE_FAILED); } pdu->type = type & ASN_TYPE_MASK; switch (pdu->type) { case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_RESPONSE: case SNMP_PDU_SET: break; case SNMP_PDU_TRAP: if (pdu->version != SNMP_V1) { snmp_error("bad pdu type %u", pdu->type); return (SNMP_CODE_FAILED); } break; case SNMP_PDU_GETBULK: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP2: case SNMP_PDU_REPORT: if (pdu->version == SNMP_V1) { snmp_error("bad pdu type %u", pdu->type); return (SNMP_CODE_FAILED); } break; default: snmp_error("bad pdu type %u", pdu->type); return (SNMP_CODE_FAILED); } trailer = b->asn_len - len; b->asn_len = len; err = parse_pdus(b, pdu, ip); if (ASN_ERR_STOPPED(err)) return (SNMP_CODE_FAILED); if (b->asn_len != 0) snmp_error("ignoring trailing junk after pdu"); b->asn_len = trailer; return (SNMP_CODE_OK); } enum snmp_code snmp_pdu_decode_secmode(struct asn_buf *b, struct snmp_pdu *pdu) { u_char type; enum snmp_code code; uint8_t digest[SNMP_USM_AUTH_SIZE]; if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH && (pdu->flags & SNMP_MSG_AUTH_FLAG) == 0) return (SNMP_CODE_BADSECLEVEL); if ((code = snmp_pdu_calc_digest(pdu, digest)) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH && memcmp(digest, pdu->msg_digest, sizeof(pdu->msg_digest)) != 0) return (SNMP_CODE_BADDIGEST); if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && (asn_get_header(b, &type, &pdu->scoped_len) != ASN_ERR_OK || type != ASN_TYPE_OCTETSTRING)) { snmp_error("cannot decode encrypted pdu"); return (SNMP_CODE_FAILED); } pdu->scoped_ptr = b->asn_ptr; if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && (pdu->flags & SNMP_MSG_PRIV_FLAG) == 0) return (SNMP_CODE_BADSECLEVEL); if ((code = snmp_pdu_decrypt(pdu)) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); return (code); } /* * Check whether what we have is the complete PDU by snooping at the * enclosing structure header. This returns: * -1 if there are ASN.1 errors * 0 if we need more data * > 0 the length of this PDU */ int snmp_pdu_snoop(const struct asn_buf *b0) { u_int length; asn_len_t len; struct asn_buf b = *b0; /* <0x10|0x20> */ if (b.asn_len == 0) return (0); if (b.asn_cptr[0] != (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED)) { asn_error(&b, "bad sequence type %u", b.asn_cptr[0]); return (-1); } b.asn_len--; b.asn_cptr++; if (b.asn_len == 0) return (0); if (*b.asn_cptr & 0x80) { /* long length */ length = *b.asn_cptr++ & 0x7f; b.asn_len--; if (length == 0) { asn_error(&b, "indefinite length not supported"); return (-1); } if (length > ASN_MAXLENLEN) { asn_error(&b, "long length too long (%u)", length); return (-1); } if (length > b.asn_len) return (0); len = 0; while (length--) { len = (len << 8) | *b.asn_cptr++; b.asn_len--; } } else { len = *b.asn_cptr++; b.asn_len--; } if (len > b.asn_len) return (0); return (len + b.asn_cptr - b0->asn_cptr); } /* * Encode the SNMP PDU without the variable bindings field. * We do this the rather uneffective way by * moving things around and assuming that the length field will never * use more than 2 bytes. * We need a number of pointers to apply the fixes afterwards. */ enum snmp_code snmp_pdu_encode_header(struct asn_buf *b, struct snmp_pdu *pdu) { enum asn_err err; u_char *v3_hdr_ptr; if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), &pdu->outer_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->version == SNMP_V1) err = asn_put_integer(b, 0); else if (pdu->version == SNMP_V2c) err = asn_put_integer(b, 1); else if (pdu->version == SNMP_V3) err = asn_put_integer(b, 3); else return (SNMP_CODE_BADVERS); if (err != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->version == SNMP_V3) { if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED), &v3_hdr_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(b, pdu->identifier) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(b, pdu->engine.max_msg_size) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->type != SNMP_PDU_RESPONSE && pdu->type != SNMP_PDU_TRAP && pdu->type != SNMP_PDU_TRAP2 && pdu->type != SNMP_PDU_REPORT) pdu->flags |= SNMP_MSG_REPORT_FLAG; if (asn_put_octetstring(b, (u_char *)&pdu->flags, 1) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_integer(b, pdu->security_model) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_commit_header(b, v3_hdr_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->security_model != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); if (pdu_encode_secparams(b, pdu) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); /* View-based Access Conntrol information */ if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED), &pdu->scoped_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_octetstring(b, (u_char *)pdu->context_engine, pdu->context_engine_len) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (asn_put_octetstring(b, (u_char *)pdu->context_name, strlen(pdu->context_name)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { if (asn_put_octetstring(b, (u_char *)pdu->community, strlen(pdu->community)) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if (asn_put_temp_header(b, (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT | pdu->type), &pdu->pdu_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->type == SNMP_PDU_TRAP) { if (pdu->version != SNMP_V1 || asn_put_objid(b, &pdu->enterprise) != ASN_ERR_OK || asn_put_ipaddress(b, pdu->agent_addr) != ASN_ERR_OK || asn_put_integer(b, pdu->generic_trap) != ASN_ERR_OK || asn_put_integer(b, pdu->specific_trap) != ASN_ERR_OK || asn_put_timeticks(b, pdu->time_stamp) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } else { if (pdu->version == SNMP_V1 && (pdu->type == SNMP_PDU_GETBULK || pdu->type == SNMP_PDU_INFORM || pdu->type == SNMP_PDU_TRAP2 || pdu->type == SNMP_PDU_REPORT)) return (SNMP_CODE_FAILED); if (asn_put_integer(b, pdu->request_id) != ASN_ERR_OK || asn_put_integer(b, pdu->error_status) != ASN_ERR_OK || asn_put_integer(b, pdu->error_index) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if (asn_put_temp_header(b, (ASN_TYPE_SEQUENCE|ASN_TYPE_CONSTRUCTED), &pdu->vars_ptr) != ASN_ERR_OK) return (SNMP_CODE_FAILED); return (SNMP_CODE_OK); } static enum asn_err snmp_pdu_fix_padd(struct asn_buf *b, struct snmp_pdu *pdu) { asn_len_t padlen; if (pdu->user.priv_proto == SNMP_PRIV_DES && pdu->scoped_len % 8 != 0) { padlen = 8 - (pdu->scoped_len % 8); if (asn_pad(b, padlen) != ASN_ERR_OK) return (ASN_ERR_FAILED); pdu->scoped_len += padlen; } return (ASN_ERR_OK); } enum snmp_code snmp_fix_encoding(struct asn_buf *b, struct snmp_pdu *pdu) { size_t moved = 0; enum snmp_code code; if (asn_commit_header(b, pdu->vars_ptr, NULL) != ASN_ERR_OK || asn_commit_header(b, pdu->pdu_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->version == SNMP_V3) { if (asn_commit_header(b, pdu->scoped_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); pdu->scoped_len = b->asn_ptr - pdu->scoped_ptr; - if ((code = snmp_pdu_fix_padd(b, pdu))!= ASN_ERR_OK) + if (snmp_pdu_fix_padd(b, pdu) != ASN_ERR_OK) return (SNMP_CODE_FAILED); if (pdu->security_model != SNMP_SECMODEL_USM) return (SNMP_CODE_FAILED); if (snmp_pdu_encrypt(pdu) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); if (pdu->user.priv_proto != SNMP_PRIV_NOPRIV && asn_commit_header(b, pdu->encrypted_ptr, NULL) != ASN_ERR_OK) return (SNMP_CODE_FAILED); } if (asn_commit_header(b, pdu->outer_ptr, &moved) != ASN_ERR_OK) return (SNMP_CODE_FAILED); pdu->outer_len = b->asn_ptr - pdu->outer_ptr; pdu->digest_ptr -= moved; if (pdu->version == SNMP_V3) { if ((code = snmp_pdu_calc_digest(pdu, pdu->msg_digest)) != SNMP_CODE_OK) return (SNMP_CODE_FAILED); if ((pdu->flags & SNMP_MSG_AUTH_FLAG) != 0) memcpy(pdu->digest_ptr, pdu->msg_digest, sizeof(pdu->msg_digest)); } return (SNMP_CODE_OK); } /* * Encode a binding. Caller must ensure, that the syntax is ok for that version. * Be sure not to cobber b, when something fails. */ enum asn_err snmp_binding_encode(struct asn_buf *b, const struct snmp_value *binding) { u_char *ptr; enum asn_err err; struct asn_buf save = *b; if ((err = asn_put_temp_header(b, (ASN_TYPE_SEQUENCE | ASN_TYPE_CONSTRUCTED), &ptr)) != ASN_ERR_OK) { *b = save; return (err); } if ((err = asn_put_objid(b, &binding->var)) != ASN_ERR_OK) { *b = save; return (err); } switch (binding->syntax) { case SNMP_SYNTAX_NULL: err = asn_put_null(b); break; case SNMP_SYNTAX_INTEGER: err = asn_put_integer(b, binding->v.integer); break; case SNMP_SYNTAX_OCTETSTRING: err = asn_put_octetstring(b, binding->v.octetstring.octets, binding->v.octetstring.len); break; case SNMP_SYNTAX_OID: err = asn_put_objid(b, &binding->v.oid); break; case SNMP_SYNTAX_IPADDRESS: err = asn_put_ipaddress(b, binding->v.ipaddress); break; case SNMP_SYNTAX_TIMETICKS: err = asn_put_uint32(b, ASN_APP_TIMETICKS, binding->v.uint32); break; case SNMP_SYNTAX_COUNTER: err = asn_put_uint32(b, ASN_APP_COUNTER, binding->v.uint32); break; case SNMP_SYNTAX_GAUGE: err = asn_put_uint32(b, ASN_APP_GAUGE, binding->v.uint32); break; case SNMP_SYNTAX_COUNTER64: err = asn_put_counter64(b, binding->v.counter64); break; case SNMP_SYNTAX_NOSUCHOBJECT: err = asn_put_exception(b, ASN_EXCEPT_NOSUCHOBJECT); break; case SNMP_SYNTAX_NOSUCHINSTANCE: err = asn_put_exception(b, ASN_EXCEPT_NOSUCHINSTANCE); break; case SNMP_SYNTAX_ENDOFMIBVIEW: err = asn_put_exception(b, ASN_EXCEPT_ENDOFMIBVIEW); break; } if (err != ASN_ERR_OK) { *b = save; return (err); } err = asn_commit_header(b, ptr, NULL); if (err != ASN_ERR_OK) { *b = save; return (err); } return (ASN_ERR_OK); } /* * Encode an PDU. */ enum snmp_code snmp_pdu_encode(struct snmp_pdu *pdu, struct asn_buf *resp_b) { u_int idx; enum snmp_code err; if ((err = snmp_pdu_encode_header(resp_b, pdu)) != SNMP_CODE_OK) return (err); for (idx = 0; idx < pdu->nbindings; idx++) - if ((err = snmp_binding_encode(resp_b, &pdu->bindings[idx])) + if (snmp_binding_encode(resp_b, &pdu->bindings[idx]) != ASN_ERR_OK) return (SNMP_CODE_FAILED); return (snmp_fix_encoding(resp_b, pdu)); } static void dump_binding(const struct snmp_value *b) { u_int i; char buf[ASN_OIDSTRLEN]; snmp_printf("%s=", asn_oid2str_r(&b->var, buf)); switch (b->syntax) { case SNMP_SYNTAX_NULL: snmp_printf("NULL"); break; case SNMP_SYNTAX_INTEGER: snmp_printf("INTEGER %d", b->v.integer); break; case SNMP_SYNTAX_OCTETSTRING: snmp_printf("OCTET STRING %lu:", b->v.octetstring.len); for (i = 0; i < b->v.octetstring.len; i++) snmp_printf(" %02x", b->v.octetstring.octets[i]); break; case SNMP_SYNTAX_OID: snmp_printf("OID %s", asn_oid2str_r(&b->v.oid, buf)); break; case SNMP_SYNTAX_IPADDRESS: snmp_printf("IPADDRESS %u.%u.%u.%u", b->v.ipaddress[0], b->v.ipaddress[1], b->v.ipaddress[2], b->v.ipaddress[3]); break; case SNMP_SYNTAX_COUNTER: snmp_printf("COUNTER %u", b->v.uint32); break; case SNMP_SYNTAX_GAUGE: snmp_printf("GAUGE %u", b->v.uint32); break; case SNMP_SYNTAX_TIMETICKS: snmp_printf("TIMETICKS %u", b->v.uint32); break; case SNMP_SYNTAX_COUNTER64: snmp_printf("COUNTER64 %lld", b->v.counter64); break; case SNMP_SYNTAX_NOSUCHOBJECT: snmp_printf("NoSuchObject"); break; case SNMP_SYNTAX_NOSUCHINSTANCE: snmp_printf("NoSuchInstance"); break; case SNMP_SYNTAX_ENDOFMIBVIEW: snmp_printf("EndOfMibView"); break; default: snmp_printf("UNKNOWN SYNTAX %u", b->syntax); break; } } static __inline void dump_bindings(const struct snmp_pdu *pdu) { u_int i; for (i = 0; i < pdu->nbindings; i++) { snmp_printf(" [%u]: ", i); dump_binding(&pdu->bindings[i]); snmp_printf("\n"); } } static __inline void dump_notrap(const struct snmp_pdu *pdu) { snmp_printf(" request_id=%d", pdu->request_id); snmp_printf(" error_status=%d", pdu->error_status); snmp_printf(" error_index=%d\n", pdu->error_index); dump_bindings(pdu); } void snmp_pdu_dump(const struct snmp_pdu *pdu) { char buf[ASN_OIDSTRLEN]; const char *vers; static const char *types[] = { [SNMP_PDU_GET] = "GET", [SNMP_PDU_GETNEXT] = "GETNEXT", [SNMP_PDU_RESPONSE] = "RESPONSE", [SNMP_PDU_SET] = "SET", [SNMP_PDU_TRAP] = "TRAPv1", [SNMP_PDU_GETBULK] = "GETBULK", [SNMP_PDU_INFORM] = "INFORM", [SNMP_PDU_TRAP2] = "TRAPv2", [SNMP_PDU_REPORT] = "REPORT", }; if (pdu->version == SNMP_V1) vers = "SNMPv1"; else if (pdu->version == SNMP_V2c) vers = "SNMPv2c"; else if (pdu->version == SNMP_V3) vers = "SNMPv3"; else vers = "v?"; switch (pdu->type) { case SNMP_PDU_TRAP: snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community); snmp_printf(" enterprise=%s", asn_oid2str_r(&pdu->enterprise, buf)); snmp_printf(" agent_addr=%u.%u.%u.%u", pdu->agent_addr[0], pdu->agent_addr[1], pdu->agent_addr[2], pdu->agent_addr[3]); snmp_printf(" generic_trap=%d", pdu->generic_trap); snmp_printf(" specific_trap=%d", pdu->specific_trap); snmp_printf(" time-stamp=%u\n", pdu->time_stamp); dump_bindings(pdu); break; case SNMP_PDU_GET: case SNMP_PDU_GETNEXT: case SNMP_PDU_RESPONSE: case SNMP_PDU_SET: case SNMP_PDU_GETBULK: case SNMP_PDU_INFORM: case SNMP_PDU_TRAP2: case SNMP_PDU_REPORT: snmp_printf("%s %s '%s'", types[pdu->type], vers, pdu->community); dump_notrap(pdu); break; default: snmp_printf("bad pdu type %u\n", pdu->type); break; } } void snmp_value_free(struct snmp_value *value) { if (value->syntax == SNMP_SYNTAX_OCTETSTRING) free(value->v.octetstring.octets); value->syntax = SNMP_SYNTAX_NULL; } int snmp_value_copy(struct snmp_value *to, const struct snmp_value *from) { to->var = from->var; to->syntax = from->syntax; if (from->syntax == SNMP_SYNTAX_OCTETSTRING) { if ((to->v.octetstring.len = from->v.octetstring.len) == 0) to->v.octetstring.octets = NULL; else { to->v.octetstring.octets = malloc(to->v.octetstring.len); if (to->v.octetstring.octets == NULL) return (-1); (void)memcpy(to->v.octetstring.octets, from->v.octetstring.octets, to->v.octetstring.len); } } else to->v = from->v; return (0); } void snmp_pdu_init_secparams(struct snmp_pdu *pdu) { int32_t rval; if (pdu->user.auth_proto != SNMP_AUTH_NOAUTH) pdu->flags |= SNMP_MSG_AUTH_FLAG; switch (pdu->user.priv_proto) { case SNMP_PRIV_DES: memcpy(pdu->msg_salt, &pdu->engine.engine_boots, sizeof(pdu->engine.engine_boots)); rval = random(); memcpy(pdu->msg_salt + sizeof(pdu->engine.engine_boots), &rval, sizeof(int32_t)); pdu->flags |= SNMP_MSG_PRIV_FLAG; break; case SNMP_PRIV_AES: rval = random(); memcpy(pdu->msg_salt, &rval, sizeof(int32_t)); rval = random(); memcpy(pdu->msg_salt + sizeof(int32_t), &rval, sizeof(int32_t)); pdu->flags |= SNMP_MSG_PRIV_FLAG; break; default: break; } } void snmp_pdu_free(struct snmp_pdu *pdu) { u_int i; for (i = 0; i < pdu->nbindings; i++) snmp_value_free(&pdu->bindings[i]); } /* * Parse an ASCII SNMP value into the binary form */ int snmp_value_parse(const char *str, enum snmp_syntax syntax, union snmp_values *v) { char *end; switch (syntax) { case SNMP_SYNTAX_NULL: case SNMP_SYNTAX_NOSUCHOBJECT: case SNMP_SYNTAX_NOSUCHINSTANCE: case SNMP_SYNTAX_ENDOFMIBVIEW: if (*str != '\0') return (-1); return (0); case SNMP_SYNTAX_INTEGER: v->integer = strtoll(str, &end, 0); if (*end != '\0') return (-1); return (0); case SNMP_SYNTAX_OCTETSTRING: { u_long len; /* actual length of string */ u_long alloc; /* allocate length of string */ u_char *octs; /* actual octets */ u_long oct; /* actual octet */ u_char *nocts; /* to avoid memory leak */ u_char c; /* actual character */ # define STUFFC(C) \ if (alloc == len) { \ alloc += 100; \ if ((nocts = realloc(octs, alloc)) == NULL) { \ free(octs); \ return (-1); \ } \ octs = nocts; \ } \ octs[len++] = (C); len = alloc = 0; octs = NULL; if (*str == '"') { str++; while((c = *str++) != '\0') { if (c == '"') { if (*str != '\0') { free(octs); return (-1); } break; } if (c == '\\') { switch (c = *str++) { case '\\': break; case 'a': c = '\a'; break; case 'b': c = '\b'; break; case 'f': c = '\f'; break; case 'n': c = '\n'; break; case 'r': c = '\r'; break; case 't': c = '\t'; break; case 'v': c = '\v'; break; case 'x': c = 0; if (!isxdigit(*str)) break; if (isdigit(*str)) c = *str++ - '0'; else if (isupper(*str)) c = *str++ - 'A' + 10; else c = *str++ - 'a' + 10; if (!isxdigit(*str)) break; if (isdigit(*str)) c += *str++ - '0'; else if (isupper(*str)) c += *str++ - 'A' + 10; else c += *str++ - 'a' + 10; break; case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': c = *str++ - '0'; if (*str < '0' || *str > '7') break; c = *str++ - '0'; if (*str < '0' || *str > '7') break; c = *str++ - '0'; break; default: break; } } STUFFC(c); } } else { while (*str != '\0') { oct = strtoul(str, &end, 16); str = end; if (oct > 0xff) { free(octs); return (-1); } STUFFC(oct); if (*str == ':') str++; else if(*str != '\0') { free(octs); return (-1); } } } v->octetstring.octets = octs; v->octetstring.len = len; return (0); # undef STUFFC } case SNMP_SYNTAX_OID: { u_long subid; v->oid.len = 0; for (;;) { if (v->oid.len == ASN_MAXOIDLEN) return (-1); subid = strtoul(str, &end, 10); str = end; if (subid > ASN_MAXID) return (-1); v->oid.subs[v->oid.len++] = (asn_subid_t)subid; if (*str == '\0') break; if (*str != '.') return (-1); str++; } return (0); } case SNMP_SYNTAX_IPADDRESS: { struct hostent *he; u_long ip[4]; int n; if (sscanf(str, "%lu.%lu.%lu.%lu%n", &ip[0], &ip[1], &ip[2], &ip[3], &n) == 4 && (size_t)n == strlen(str) && ip[0] <= 0xff && ip[1] <= 0xff && ip[2] <= 0xff && ip[3] <= 0xff) { v->ipaddress[0] = (u_char)ip[0]; v->ipaddress[1] = (u_char)ip[1]; v->ipaddress[2] = (u_char)ip[2]; v->ipaddress[3] = (u_char)ip[3]; return (0); } if ((he = gethostbyname(str)) == NULL) return (-1); if (he->h_addrtype != AF_INET) return (-1); v->ipaddress[0] = he->h_addr[0]; v->ipaddress[1] = he->h_addr[1]; v->ipaddress[2] = he->h_addr[2]; v->ipaddress[3] = he->h_addr[3]; return (0); } case SNMP_SYNTAX_COUNTER: case SNMP_SYNTAX_GAUGE: case SNMP_SYNTAX_TIMETICKS: { uint64_t sub; sub = strtoull(str, &end, 0); if (*end != '\0' || sub > 0xffffffff) return (-1); v->uint32 = (uint32_t)sub; return (0); } case SNMP_SYNTAX_COUNTER64: v->counter64 = strtoull(str, &end, 0); if (*end != '\0') return (-1); return (0); } abort(); } static void snmp_error_func(const char *fmt, ...) { va_list ap; va_start(ap, fmt); fprintf(stderr, "SNMP: "); vfprintf(stderr, fmt, ap); fprintf(stderr, "\n"); va_end(ap); } static void snmp_printf_func(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); } Index: stable/9/contrib/bsnmp/lib/snmp.h =================================================================== --- stable/9/contrib/bsnmp/lib/snmp.h (revision 301661) +++ stable/9/contrib/bsnmp/lib/snmp.h (revision 301662) @@ -1,284 +1,284 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * Portions of this software were developed by Shteryana Sotirova Shopova * under sponsorship from the FreeBSD Foundation. * * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/lib/snmp.h,v 1.30 2004/08/06 08:46:54 brandt Exp $ * * Header file for SNMP functions. */ #ifndef snmp_h_ #define snmp_h_ #include #define SNMP_COMMUNITY_MAXLEN 128 #define SNMP_MAX_BINDINGS 100 #define SNMP_CONTEXT_NAME_SIZ (32 + 1) #define SNMP_ENGINE_ID_SIZ 32 #define SNMP_TIME_WINDOW 150 enum snmp_syntax { SNMP_SYNTAX_NULL = 0, SNMP_SYNTAX_INTEGER, /* == INTEGER32 */ SNMP_SYNTAX_OCTETSTRING, SNMP_SYNTAX_OID, SNMP_SYNTAX_IPADDRESS, SNMP_SYNTAX_COUNTER, SNMP_SYNTAX_GAUGE, /* == UNSIGNED32 */ SNMP_SYNTAX_TIMETICKS, /* v2 additions */ SNMP_SYNTAX_COUNTER64, SNMP_SYNTAX_NOSUCHOBJECT, /* exception */ SNMP_SYNTAX_NOSUCHINSTANCE, /* exception */ SNMP_SYNTAX_ENDOFMIBVIEW, /* exception */ }; struct snmp_value { struct asn_oid var; enum snmp_syntax syntax; union snmp_values { int32_t integer; /* also integer32 */ struct { u_int len; u_char *octets; } octetstring; struct asn_oid oid; u_char ipaddress[4]; uint32_t uint32; /* also gauge32, counter32, unsigned32, timeticks */ uint64_t counter64; } v; }; enum snmp_version { SNMP_Verr = 0, SNMP_V1 = 1, SNMP_V2c = 2, SNMP_V3, }; #define SNMP_MPM_SNMP_V1 0 #define SNMP_MPM_SNMP_V2c 1 #define SNMP_MPM_SNMP_V3 3 #define SNMP_ADM_STR32_SIZ (32 + 1) #define SNMP_AUTH_KEY_SIZ 40 #define SNMP_PRIV_KEY_SIZ 32 #define SNMP_USM_AUTH_SIZE 12 #define SNMP_USM_PRIV_SIZE 8 #define SNMP_AUTH_HMACMD5_KEY_SIZ 16 #define SNMP_AUTH_HMACSHA_KEY_SIZ 20 #define SNMP_PRIV_AES_KEY_SIZ 16 #define SNMP_PRIV_DES_KEY_SIZ 8 enum snmp_secmodel { SNMP_SECMODEL_ANY = 0, SNMP_SECMODEL_SNMPv1 = 1, SNMP_SECMODEL_SNMPv2c = 2, SNMP_SECMODEL_USM = 3, SNMP_SECMODEL_UNKNOWN }; enum snmp_usm_level { SNMP_noAuthNoPriv = 1, SNMP_authNoPriv = 2, SNMP_authPriv = 3 }; enum snmp_authentication { SNMP_AUTH_NOAUTH = 0, SNMP_AUTH_HMAC_MD5, SNMP_AUTH_HMAC_SHA }; enum snmp_privacy { SNMP_PRIV_NOPRIV = 0, SNMP_PRIV_DES = 1, SNMP_PRIV_AES }; struct snmp_engine { uint8_t engine_id[SNMP_ENGINE_ID_SIZ]; uint32_t engine_len; int32_t engine_boots; int32_t engine_time; int32_t max_msg_size; }; struct snmp_user { char sec_name[SNMP_ADM_STR32_SIZ]; enum snmp_authentication auth_proto; enum snmp_privacy priv_proto; uint8_t auth_key[SNMP_AUTH_KEY_SIZ]; uint8_t priv_key[SNMP_PRIV_KEY_SIZ]; }; struct snmp_pdu { char community[SNMP_COMMUNITY_MAXLEN + 1]; enum snmp_version version; u_int type; /* SNMPv3 PDU header fields */ int32_t identifier; uint8_t flags; int32_t security_model; struct snmp_engine engine; /* Associated USM user parameters */ struct snmp_user user; uint8_t msg_digest[SNMP_USM_AUTH_SIZE]; uint8_t msg_salt[SNMP_USM_PRIV_SIZE]; /* View-based Access Model */ /* XXX: put in separate structure - conflicts with struct snmp_context */ uint32_t context_engine_len; uint8_t context_engine[SNMP_ENGINE_ID_SIZ]; char context_name[SNMP_CONTEXT_NAME_SIZ]; /* trap only */ struct asn_oid enterprise; u_char agent_addr[4]; int32_t generic_trap; int32_t specific_trap; uint32_t time_stamp; /* others */ int32_t request_id; int32_t error_status; int32_t error_index; /* fixes for encoding */ size_t outer_len; - size_t scoped_len; + asn_len_t scoped_len; u_char *outer_ptr; u_char *digest_ptr; u_char *encrypted_ptr; u_char *scoped_ptr; u_char *pdu_ptr; u_char *vars_ptr; struct snmp_value bindings[SNMP_MAX_BINDINGS]; u_int nbindings; }; #define snmp_v1_pdu snmp_pdu #define SNMP_PDU_GET 0 #define SNMP_PDU_GETNEXT 1 #define SNMP_PDU_RESPONSE 2 #define SNMP_PDU_SET 3 #define SNMP_PDU_TRAP 4 /* v1 */ #define SNMP_PDU_GETBULK 5 /* v2 */ #define SNMP_PDU_INFORM 6 /* v2 */ #define SNMP_PDU_TRAP2 7 /* v2 */ #define SNMP_PDU_REPORT 8 /* v2 */ #define SNMP_ERR_NOERROR 0 #define SNMP_ERR_TOOBIG 1 #define SNMP_ERR_NOSUCHNAME 2 /* v1 */ #define SNMP_ERR_BADVALUE 3 /* v1 */ #define SNMP_ERR_READONLY 4 /* v1 */ #define SNMP_ERR_GENERR 5 #define SNMP_ERR_NO_ACCESS 6 /* v2 */ #define SNMP_ERR_WRONG_TYPE 7 /* v2 */ #define SNMP_ERR_WRONG_LENGTH 8 /* v2 */ #define SNMP_ERR_WRONG_ENCODING 9 /* v2 */ #define SNMP_ERR_WRONG_VALUE 10 /* v2 */ #define SNMP_ERR_NO_CREATION 11 /* v2 */ #define SNMP_ERR_INCONS_VALUE 12 /* v2 */ #define SNMP_ERR_RES_UNAVAIL 13 /* v2 */ #define SNMP_ERR_COMMIT_FAILED 14 /* v2 */ #define SNMP_ERR_UNDO_FAILED 15 /* v2 */ #define SNMP_ERR_AUTH_ERR 16 /* v2 */ #define SNMP_ERR_NOT_WRITEABLE 17 /* v2 */ #define SNMP_ERR_INCONS_NAME 18 /* v2 */ #define SNMP_TRAP_COLDSTART 0 #define SNMP_TRAP_WARMSTART 1 #define SNMP_TRAP_LINKDOWN 2 #define SNMP_TRAP_LINKUP 3 #define SNMP_TRAP_AUTHENTICATION_FAILURE 4 #define SNMP_TRAP_EGP_NEIGHBOR_LOSS 5 #define SNMP_TRAP_ENTERPRISE 6 enum snmp_code { SNMP_CODE_OK = 0, SNMP_CODE_FAILED, SNMP_CODE_BADVERS, SNMP_CODE_BADLEN, SNMP_CODE_BADENC, SNMP_CODE_OORANGE, SNMP_CODE_BADSECLEVEL, SNMP_CODE_NOTINTIME, SNMP_CODE_BADUSER, SNMP_CODE_BADENGINE, SNMP_CODE_BADDIGEST, SNMP_CODE_EDECRYPT }; #define SNMP_MSG_AUTH_FLAG 0x1 #define SNMP_MSG_PRIV_FLAG 0x2 #define SNMP_MSG_REPORT_FLAG 0x4 #define SNMP_MSG_AUTODISCOVER 0x80 void snmp_value_free(struct snmp_value *); int snmp_value_parse(const char *, enum snmp_syntax, union snmp_values *); int snmp_value_copy(struct snmp_value *, const struct snmp_value *); void snmp_pdu_free(struct snmp_pdu *); void snmp_pdu_init_secparams(struct snmp_pdu *); enum snmp_code snmp_pdu_decode(struct asn_buf *b, struct snmp_pdu *pdu, int32_t *); enum snmp_code snmp_pdu_decode_header(struct asn_buf *, struct snmp_pdu *); enum snmp_code snmp_pdu_decode_scoped(struct asn_buf *, struct snmp_pdu *, int32_t *); enum snmp_code snmp_pdu_encode(struct snmp_pdu *, struct asn_buf *); enum snmp_code snmp_pdu_decode_secmode(struct asn_buf *, struct snmp_pdu *); int snmp_pdu_snoop(const struct asn_buf *); void snmp_pdu_dump(const struct snmp_pdu *pdu); enum snmp_code snmp_passwd_to_keys(struct snmp_user *, char *); enum snmp_code snmp_get_local_keys(struct snmp_user *, uint8_t *, uint32_t); enum snmp_code snmp_calc_keychange(struct snmp_user *, uint8_t *); extern void (*snmp_error)(const char *, ...); extern void (*snmp_printf)(const char *, ...); #define TRUTH_MK(F) ((F) ? 1 : 2) #define TRUTH_GET(T) (((T) == 1) ? 1 : 0) #define TRUTH_OK(T) ((T) == 1 || (T) == 2) #endif Index: stable/9/contrib/bsnmp/lib/snmpagent.c =================================================================== --- stable/9/contrib/bsnmp/lib/snmpagent.c (revision 301661) +++ stable/9/contrib/bsnmp/lib/snmpagent.c (revision 301662) @@ -1,1012 +1,1067 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/lib/snmpagent.c,v 1.20 2005/10/04 11:21:33 brandt_h Exp $ * * SNMP Agent functions */ #include #include #include #include #include #include #ifdef HAVE_STDINT_H #include #elif defined(HAVE_INTTYPES_H) #include #endif #include #include "asn1.h" #include "snmp.h" #include "snmppriv.h" #include "snmpagent.h" static void snmp_debug_func(const char *fmt, ...); void (*snmp_debug)(const char *fmt, ...) = snmp_debug_func; struct snmp_node *tree; u_int tree_size; /* * Structure to hold dependencies during SET processing * The last two members of this structure must be the * dependency visible by the user and the user data. */ struct depend { TAILQ_ENTRY(depend) link; size_t len; /* size of data part */ snmp_depop_t func; struct snmp_dependency dep; #if defined(__GNUC__) && __GNUC__ < 3 u_char data[0]; #else u_char data[]; #endif }; TAILQ_HEAD(depend_list, depend); /* * Set context */ struct context { struct snmp_context ctx; struct depend_list dlist; const struct snmp_node *node[SNMP_MAX_BINDINGS]; struct snmp_scratch scratch[SNMP_MAX_BINDINGS]; struct depend *depend; }; #define TR(W) (snmp_trace & SNMP_TRACE_##W) u_int snmp_trace = 0; static char oidbuf[ASN_OIDSTRLEN]; /* * Allocate a context */ struct snmp_context * snmp_init_context(void) { struct context *context; if ((context = malloc(sizeof(*context))) == NULL) return (NULL); memset(context, 0, sizeof(*context)); TAILQ_INIT(&context->dlist); return (&context->ctx); } /* * Find a variable for SET/GET and the first GETBULK pass. * Return the node pointer. If the search fails, set the errp to * the correct SNMPv2 GET exception code. */ static struct snmp_node * find_node(const struct snmp_value *value, enum snmp_syntax *errp) { struct snmp_node *tp; if (TR(FIND)) snmp_debug("find: searching %s", asn_oid2str_r(&value->var, oidbuf)); /* * If we have an exact match (the entry in the table is a * sub-oid from the variable) we have found what we are for. * If the table oid is higher than the variable, there is no match. */ for (tp = tree; tp < tree + tree_size; tp++) { if (asn_is_suboid(&tp->oid, &value->var)) goto found; if (asn_compare_oid(&tp->oid, &value->var) >= 0) break; } if (TR(FIND)) snmp_debug("find: no match"); *errp = SNMP_SYNTAX_NOSUCHOBJECT; return (NULL); found: /* leafs must have a 0 instance identifier */ if (tp->type == SNMP_NODE_LEAF && (value->var.len != tp->oid.len + 1 || value->var.subs[tp->oid.len] != 0)) { if (TR(FIND)) snmp_debug("find: bad leaf index"); *errp = SNMP_SYNTAX_NOSUCHINSTANCE; return (NULL); } if (TR(FIND)) snmp_debug("find: found %s", asn_oid2str_r(&value->var, oidbuf)); return (tp); } static struct snmp_node * find_subnode(const struct snmp_value *value) { struct snmp_node *tp; for (tp = tree; tp < tree + tree_size; tp++) { if (asn_is_suboid(&value->var, &tp->oid)) return (tp); } return (NULL); } static void -snmp_pdu_create_response(struct snmp_pdu *pdu, struct snmp_pdu *resp) +snmp_pdu_create_response(const struct snmp_pdu *pdu, struct snmp_pdu *resp) { memset(resp, 0, sizeof(*resp)); strcpy(resp->community, pdu->community); resp->version = pdu->version; resp->type = SNMP_PDU_RESPONSE; resp->request_id = pdu->request_id; resp->version = pdu->version; if (resp->version != SNMP_V3) return; memcpy(&resp->engine, &pdu->engine, sizeof(pdu->engine)); memcpy(&resp->user, &pdu->user, sizeof(pdu->user)); snmp_pdu_init_secparams(resp); resp->identifier = pdu->identifier; resp->security_model = pdu->security_model; resp->context_engine_len = pdu->context_engine_len; memcpy(resp->context_engine, pdu->context_engine, resp->context_engine_len); strlcpy(resp->context_name, pdu->context_name, sizeof(resp->context_name)); } /* * Execute a GET operation. The tree is rooted at the global 'root'. * Build the response PDU on the fly. If the return code is SNMP_RET_ERR * the pdu error status and index will be set. */ enum snmp_ret snmp_get(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *data) { int ret; u_int i; struct snmp_node *tp; enum snmp_syntax except; struct context context; enum asn_err err; memset(&context, 0, sizeof(context)); context.ctx.data = data; snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) /* cannot even encode header - very bad */ return (SNMP_RET_IGN); for (i = 0; i < pdu->nbindings; i++) { resp->bindings[i].var = pdu->bindings[i].var; if ((tp = find_node(&pdu->bindings[i], &except)) == NULL) { if (pdu->version == SNMP_V1) { if (TR(GET)) snmp_debug("get: nosuchname"); pdu->error_status = SNMP_ERR_NOSUCHNAME; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } if (TR(GET)) snmp_debug("get: exception %u", except); resp->bindings[i].syntax = except; } else { /* call the action to fetch the value. */ resp->bindings[i].syntax = tp->syntax; ret = (*tp->op)(&context.ctx, &resp->bindings[i], tp->oid.len, tp->index, SNMP_OP_GET); if (TR(GET)) snmp_debug("get: action returns %d", ret); if (ret == SNMP_ERR_NOSUCHNAME) { if (pdu->version == SNMP_V1) { pdu->error_status = SNMP_ERR_NOSUCHNAME; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } if (TR(GET)) snmp_debug("get: exception noSuchInstance"); resp->bindings[i].syntax = SNMP_SYNTAX_NOSUCHINSTANCE; } else if (ret != SNMP_ERR_NOERROR) { pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } resp->nbindings++; err = snmp_binding_encode(resp_b, &resp->bindings[i]); if (err == ASN_ERR_EOBUF) { pdu->error_status = SNMP_ERR_TOOBIG; pdu->error_index = 0; snmp_pdu_free(resp); return (SNMP_RET_ERR); } if (err != ASN_ERR_OK) { if (TR(GET)) snmp_debug("get: binding encoding: %u", err); pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } - return (snmp_fix_encoding(resp_b, resp)); + if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { + snmp_debug("get: failed to encode PDU"); + return (SNMP_RET_ERR); + } + + return (SNMP_RET_OK); } static struct snmp_node * next_node(const struct snmp_value *value, int *pnext) { struct snmp_node *tp; if (TR(FIND)) snmp_debug("next: searching %s", asn_oid2str_r(&value->var, oidbuf)); *pnext = 0; for (tp = tree; tp < tree + tree_size; tp++) { if (asn_is_suboid(&tp->oid, &value->var)) { /* the tree OID is a sub-oid of the requested OID. */ if (tp->type == SNMP_NODE_LEAF) { if (tp->oid.len == value->var.len) { /* request for scalar type */ if (TR(FIND)) snmp_debug("next: found scalar %s", asn_oid2str_r(&tp->oid, oidbuf)); return (tp); } /* try next */ } else { if (TR(FIND)) snmp_debug("next: found column %s", asn_oid2str_r(&tp->oid, oidbuf)); return (tp); } } else if (asn_is_suboid(&value->var, &tp->oid) || asn_compare_oid(&tp->oid, &value->var) >= 0) { if (TR(FIND)) snmp_debug("next: found %s", asn_oid2str_r(&tp->oid, oidbuf)); *pnext = 1; return (tp); } } if (TR(FIND)) snmp_debug("next: failed"); return (NULL); } static enum snmp_ret do_getnext(struct context *context, const struct snmp_value *inb, struct snmp_value *outb, struct snmp_pdu *pdu) { const struct snmp_node *tp; int ret, next; if ((tp = next_node(inb, &next)) == NULL) goto eofMib; /* retain old variable if we are doing a GETNEXT on an exact * matched leaf only */ if (tp->type == SNMP_NODE_LEAF || next) outb->var = tp->oid; else outb->var = inb->var; for (;;) { outb->syntax = tp->syntax; if (tp->type == SNMP_NODE_LEAF) { /* make a GET operation */ outb->var.subs[outb->var.len++] = 0; ret = (*tp->op)(&context->ctx, outb, tp->oid.len, tp->index, SNMP_OP_GET); } else { /* make a GETNEXT */ ret = (*tp->op)(&context->ctx, outb, tp->oid.len, tp->index, SNMP_OP_GETNEXT); } if (ret != SNMP_ERR_NOSUCHNAME) { /* got something */ if (ret != SNMP_ERR_NOERROR && TR(GETNEXT)) snmp_debug("getnext: %s returns %u", asn_oid2str(&outb->var), ret); break; } /* object has no data - try next */ if (++tp == tree + tree_size) break; if (TR(GETNEXT)) snmp_debug("getnext: no data - avancing to %s", asn_oid2str(&tp->oid)); outb->var = tp->oid; } if (ret == SNMP_ERR_NOSUCHNAME) { eofMib: outb->var = inb->var; if (pdu->version == SNMP_V1) { pdu->error_status = SNMP_ERR_NOSUCHNAME; return (SNMP_RET_ERR); } outb->syntax = SNMP_SYNTAX_ENDOFMIBVIEW; } else if (ret != SNMP_ERR_NOERROR) { pdu->error_status = SNMP_ERR_GENERR; return (SNMP_RET_ERR); } return (SNMP_RET_OK); } /* * Execute a GETNEXT operation. The tree is rooted at the global 'root'. * Build the response PDU on the fly. The return is: */ enum snmp_ret snmp_getnext(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *data) { struct context context; u_int i; enum asn_err err; enum snmp_ret result; memset(&context, 0, sizeof(context)); context.ctx.data = data; snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp)) return (SNMP_RET_IGN); for (i = 0; i < pdu->nbindings; i++) { result = do_getnext(&context, &pdu->bindings[i], &resp->bindings[i], pdu); if (result != SNMP_RET_OK) { pdu->error_index = i + 1; snmp_pdu_free(resp); return (result); } resp->nbindings++; err = snmp_binding_encode(resp_b, &resp->bindings[i]); if (err == ASN_ERR_EOBUF) { pdu->error_status = SNMP_ERR_TOOBIG; pdu->error_index = 0; snmp_pdu_free(resp); return (SNMP_RET_ERR); } if (err != ASN_ERR_OK) { if (TR(GET)) snmp_debug("getnext: binding encoding: %u", err); pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } - return (snmp_fix_encoding(resp_b, resp)); + + if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { + snmp_debug("getnext: failed to encode PDU"); + return (SNMP_RET_ERR); + } + + return (SNMP_RET_OK); } enum snmp_ret snmp_getbulk(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *data) { struct context context; u_int i; int cnt; u_int non_rep; int eomib; enum snmp_ret result; enum asn_err err; memset(&context, 0, sizeof(context)); context.ctx.data = data; snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp) != SNMP_CODE_OK) /* cannot even encode header - very bad */ return (SNMP_RET_IGN); if ((non_rep = pdu->error_status) > pdu->nbindings) non_rep = pdu->nbindings; /* non-repeaters */ for (i = 0; i < non_rep; i++) { result = do_getnext(&context, &pdu->bindings[i], &resp->bindings[resp->nbindings], pdu); if (result != SNMP_RET_OK) { pdu->error_index = i + 1; snmp_pdu_free(resp); return (result); } err = snmp_binding_encode(resp_b, &resp->bindings[resp->nbindings++]); if (err == ASN_ERR_EOBUF) goto done; if (err != ASN_ERR_OK) { if (TR(GET)) snmp_debug("getnext: binding encoding: %u", err); pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } if (non_rep == pdu->nbindings) goto done; /* repeates */ for (cnt = 0; cnt < pdu->error_index; cnt++) { eomib = 1; for (i = non_rep; i < pdu->nbindings; i++) { if (resp->nbindings == SNMP_MAX_BINDINGS) /* PDU is full */ goto done; if (cnt == 0) result = do_getnext(&context, &pdu->bindings[i], &resp->bindings[resp->nbindings], pdu); else result = do_getnext(&context, &resp->bindings[resp->nbindings - (pdu->nbindings - non_rep)], &resp->bindings[resp->nbindings], pdu); if (result != SNMP_RET_OK) { pdu->error_index = i + 1; snmp_pdu_free(resp); return (result); } if (resp->bindings[resp->nbindings].syntax != SNMP_SYNTAX_ENDOFMIBVIEW) eomib = 0; err = snmp_binding_encode(resp_b, &resp->bindings[resp->nbindings++]); if (err == ASN_ERR_EOBUF) goto done; if (err != ASN_ERR_OK) { if (TR(GET)) snmp_debug("getnext: binding encoding: %u", err); pdu->error_status = SNMP_ERR_GENERR; pdu->error_index = i + 1; snmp_pdu_free(resp); return (SNMP_RET_ERR); } } if (eomib) break; } done: - return (snmp_fix_encoding(resp_b, resp)); + if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { + snmp_debug("getnext: failed to encode PDU"); + return (SNMP_RET_ERR); + } + + return (SNMP_RET_OK); } /* * Rollback a SET operation. Failed index is 'i'. */ static void rollback(struct context *context, struct snmp_pdu *pdu, u_int i) { struct snmp_value *b; const struct snmp_node *np; int ret; while (i-- > 0) { b = &pdu->bindings[i]; np = context->node[i]; context->ctx.scratch = &context->scratch[i]; ret = (*np->op)(&context->ctx, b, np->oid.len, np->index, SNMP_OP_ROLLBACK); if (ret != SNMP_ERR_NOERROR) { snmp_error("set: rollback failed (%d) on variable %s " "index %u", ret, asn_oid2str(&b->var), i); if (pdu->version != SNMP_V1) { pdu->error_status = SNMP_ERR_UNDO_FAILED; pdu->error_index = 0; } } } } /* * Commit dependencies. */ int snmp_dep_commit(struct snmp_context *ctx) { struct context *context = (struct context *)ctx; int ret; TAILQ_FOREACH(context->depend, &context->dlist, link) { ctx->dep = &context->depend->dep; if (TR(SET)) snmp_debug("set: dependency commit %s", asn_oid2str(&ctx->dep->obj)); ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_COMMIT); if (ret != SNMP_ERR_NOERROR) { if (TR(SET)) snmp_debug("set: dependency failed %d", ret); return (ret); } } return (SNMP_ERR_NOERROR); } /* * Rollback dependencies */ int snmp_dep_rollback(struct snmp_context *ctx) { struct context *context = (struct context *)ctx; int ret, ret1; char objbuf[ASN_OIDSTRLEN]; char idxbuf[ASN_OIDSTRLEN]; ret1 = SNMP_ERR_NOERROR; while ((context->depend = TAILQ_PREV(context->depend, depend_list, link)) != NULL) { ctx->dep = &context->depend->dep; if (TR(SET)) snmp_debug("set: dependency rollback %s", asn_oid2str(&ctx->dep->obj)); ret = context->depend->func(ctx, ctx->dep, SNMP_DEPOP_ROLLBACK); if (ret != SNMP_ERR_NOERROR) { snmp_debug("set: dep rollback returns %u: %s %s", ret, asn_oid2str_r(&ctx->dep->obj, objbuf), asn_oid2str_r(&ctx->dep->idx, idxbuf)); if (ret1 == SNMP_ERR_NOERROR) ret1 = ret; } } return (ret1); } void snmp_dep_finish(struct snmp_context *ctx) { struct context *context = (struct context *)ctx; struct depend *d; while ((d = TAILQ_FIRST(&context->dlist)) != NULL) { ctx->dep = &d->dep; (void)d->func(ctx, ctx->dep, SNMP_DEPOP_FINISH); TAILQ_REMOVE(&context->dlist, d, link); free(d); } } /* * Do a SET operation. */ enum snmp_ret snmp_set(struct snmp_pdu *pdu, struct asn_buf *resp_b, struct snmp_pdu *resp, void *data) { int ret; u_int i; enum asn_err asnerr; struct context context; const struct snmp_node *np; struct snmp_value *b; enum snmp_syntax except; memset(&context, 0, sizeof(context)); TAILQ_INIT(&context.dlist); context.ctx.data = data; snmp_pdu_create_response(pdu, resp); if (snmp_pdu_encode_header(resp_b, resp)) return (SNMP_RET_IGN); /* * 1. Find all nodes, check that they are writeable and * that the syntax is ok, copy over the binding to the response. */ for (i = 0; i < pdu->nbindings; i++) { b = &pdu->bindings[i]; if ((np = context.node[i] = find_node(b, &except)) == NULL) { /* not found altogether or LEAF with wrong index */ if (TR(SET)) snmp_debug("set: node not found %s", asn_oid2str_r(&b->var, oidbuf)); if (pdu->version == SNMP_V1) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NOSUCHNAME; } else if ((np = find_subnode(b)) != NULL) { /* 2. intermediate object */ pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NOT_WRITEABLE; } else if (except == SNMP_SYNTAX_NOSUCHOBJECT) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NO_ACCESS; } else { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NO_CREATION; } snmp_pdu_free(resp); return (SNMP_RET_ERR); } /* * 2. write/createable? * Can check this for leafs only, because in v2 we have * to differentiate between NOT_WRITEABLE and NO_CREATION * and only the action routine for COLUMNS knows, whether * a column exists. */ if (np->type == SNMP_NODE_LEAF && !(np->flags & SNMP_NODE_CANSET)) { if (pdu->version == SNMP_V1) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NOSUCHNAME; } else { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_NOT_WRITEABLE; } snmp_pdu_free(resp); return (SNMP_RET_ERR); } /* * 3. Ensure the right syntax */ if (np->syntax != b->syntax) { if (pdu->version == SNMP_V1) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_BADVALUE; /* v2: wrongType */ } else { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_WRONG_TYPE; } snmp_pdu_free(resp); return (SNMP_RET_ERR); } /* * 4. Copy binding */ if (snmp_value_copy(&resp->bindings[i], b)) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_GENERR; snmp_pdu_free(resp); return (SNMP_RET_ERR); } asnerr = snmp_binding_encode(resp_b, &resp->bindings[i]); if (asnerr == ASN_ERR_EOBUF) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_TOOBIG; snmp_pdu_free(resp); return (SNMP_RET_ERR); } else if (asnerr != ASN_ERR_OK) { pdu->error_index = i + 1; pdu->error_status = SNMP_ERR_GENERR; snmp_pdu_free(resp); return (SNMP_RET_ERR); } resp->nbindings++; } context.ctx.code = SNMP_RET_OK; /* * 2. Call the SET method for each node. If a SET fails, rollback * everything. Map error codes depending on the version. */ for (i = 0; i < pdu->nbindings; i++) { b = &pdu->bindings[i]; np = context.node[i]; context.ctx.var_index = i + 1; context.ctx.scratch = &context.scratch[i]; ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, SNMP_OP_SET); if (TR(SET)) snmp_debug("set: action %s returns %d", np->name, ret); if (pdu->version == SNMP_V1) { switch (ret) { case SNMP_ERR_NO_ACCESS: ret = SNMP_ERR_NOSUCHNAME; break; case SNMP_ERR_WRONG_TYPE: /* should no happen */ ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_WRONG_LENGTH: ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_WRONG_ENCODING: /* should not happen */ ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_WRONG_VALUE: ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_NO_CREATION: ret = SNMP_ERR_NOSUCHNAME; break; case SNMP_ERR_INCONS_VALUE: ret = SNMP_ERR_BADVALUE; break; case SNMP_ERR_RES_UNAVAIL: ret = SNMP_ERR_GENERR; break; case SNMP_ERR_COMMIT_FAILED: ret = SNMP_ERR_GENERR; break; case SNMP_ERR_UNDO_FAILED: ret = SNMP_ERR_GENERR; break; case SNMP_ERR_AUTH_ERR: /* should not happen */ ret = SNMP_ERR_GENERR; break; case SNMP_ERR_NOT_WRITEABLE: ret = SNMP_ERR_NOSUCHNAME; break; case SNMP_ERR_INCONS_NAME: ret = SNMP_ERR_BADVALUE; break; } } if (ret != SNMP_ERR_NOERROR) { pdu->error_index = i + 1; pdu->error_status = ret; rollback(&context, pdu, i); snmp_pdu_free(resp); context.ctx.code = SNMP_RET_ERR; goto errout; } } /* * 3. Call dependencies */ if (TR(SET)) snmp_debug("set: set operations ok"); if ((ret = snmp_dep_commit(&context.ctx)) != SNMP_ERR_NOERROR) { pdu->error_status = ret; pdu->error_index = context.ctx.var_index; if ((ret = snmp_dep_rollback(&context.ctx)) != SNMP_ERR_NOERROR) { if (pdu->version != SNMP_V1) { pdu->error_status = SNMP_ERR_UNDO_FAILED; pdu->error_index = 0; } } rollback(&context, pdu, i); snmp_pdu_free(resp); context.ctx.code = SNMP_RET_ERR; goto errout; } /* * 4. Commit and copy values from the original packet to the response. * This is not the commit operation from RFC 1905 but rather an * 'FREE RESOURCES' operation. It shouldn't fail. */ if (TR(SET)) snmp_debug("set: commiting"); for (i = 0; i < pdu->nbindings; i++) { b = &resp->bindings[i]; np = context.node[i]; context.ctx.var_index = i + 1; context.ctx.scratch = &context.scratch[i]; ret = (*np->op)(&context.ctx, b, np->oid.len, np->index, SNMP_OP_COMMIT); if (ret != SNMP_ERR_NOERROR) snmp_error("set: commit failed (%d) on" " variable %s index %u", ret, asn_oid2str_r(&b->var, oidbuf), i); } if (snmp_fix_encoding(resp_b, resp) != SNMP_CODE_OK) { snmp_error("set: fix_encoding failed"); snmp_pdu_free(resp); context.ctx.code = SNMP_RET_IGN; } /* * Done */ errout: snmp_dep_finish(&context.ctx); if (TR(SET)) snmp_debug("set: returning %d", context.ctx.code); return (context.ctx.code); } /* * Lookup a dependency. If it doesn't exist, create one */ struct snmp_dependency * snmp_dep_lookup(struct snmp_context *ctx, const struct asn_oid *obj, const struct asn_oid *idx, size_t len, snmp_depop_t func) { struct context *context; struct depend *d; context = (struct context *)(void *) ((char *)ctx - offsetof(struct context, ctx)); if (TR(DEPEND)) { snmp_debug("depend: looking for %s", asn_oid2str(obj)); if (idx) snmp_debug("depend: index is %s", asn_oid2str(idx)); } TAILQ_FOREACH(d, &context->dlist, link) if (asn_compare_oid(obj, &d->dep.obj) == 0 && ((idx == NULL && d->dep.idx.len == 0) || (idx != NULL && asn_compare_oid(idx, &d->dep.idx) == 0))) { if(TR(DEPEND)) snmp_debug("depend: found"); return (&d->dep); } if(TR(DEPEND)) snmp_debug("depend: creating"); if ((d = malloc(offsetof(struct depend, dep) + len)) == NULL) return (NULL); memset(&d->dep, 0, len); d->dep.obj = *obj; if (idx == NULL) d->dep.idx.len = 0; else d->dep.idx = *idx; d->len = len; d->func = func; TAILQ_INSERT_TAIL(&context->dlist, d, link); return (&d->dep); } /* * Make an error response from a PDU. We do this without decoding the * variable bindings. This means we can sent the junk back to a caller * that has sent us junk in the first place. */ enum snmp_ret snmp_make_errresp(const struct snmp_pdu *pdu, struct asn_buf *pdu_b, struct asn_buf *resp_b) { + u_char type; asn_len_t len; struct snmp_pdu resp; enum asn_err err; enum snmp_code code; - memset(&resp, 0, sizeof(resp)); + snmp_pdu_create_response(pdu, &resp); + if ((code = snmp_pdu_decode_header(pdu_b, &resp)) != SNMP_CODE_OK) return (SNMP_RET_IGN); - if (pdu_b->asn_len < len) + if (pdu->version == SNMP_V3) { + if (resp.user.priv_proto != SNMP_PRIV_NOPRIV && + (asn_get_header(pdu_b, &type, &resp.scoped_len) != ASN_ERR_OK + || type != ASN_TYPE_OCTETSTRING)) { + snmp_error("cannot decode encrypted pdu"); + return (SNMP_RET_IGN); + } + + if (asn_get_sequence(pdu_b, &len) != ASN_ERR_OK) { + snmp_error("cannot decode scoped pdu header"); + return (SNMP_RET_IGN); + } + + len = SNMP_ENGINE_ID_SIZ; + if (asn_get_octetstring(pdu_b, (u_char *)resp.context_engine, + &len) != ASN_ERR_OK) { + snmp_error("cannot decode msg context engine"); + return (SNMP_RET_IGN); + } + resp.context_engine_len = len; + len = SNMP_CONTEXT_NAME_SIZ; + if (asn_get_octetstring(pdu_b, (u_char *)resp.context_name, + &len) != ASN_ERR_OK) { + snmp_error("cannot decode msg context name"); + return (SNMP_RET_IGN); + } + resp.context_name[len] = '\0'; + } + + + if (asn_get_header(pdu_b, &type, &len) != ASN_ERR_OK) { + snmp_error("cannot get pdu header"); return (SNMP_RET_IGN); - pdu_b->asn_len = len; + } + + if ((type & ~ASN_TYPE_MASK) != + (ASN_TYPE_CONSTRUCTED | ASN_CLASS_CONTEXT)) { + snmp_error("bad pdu header tag"); + return (SNMP_RET_IGN); + } err = snmp_parse_pdus_hdr(pdu_b, &resp, &len); if (ASN_ERR_STOPPED(err)) return (SNMP_RET_IGN); if (pdu_b->asn_len < len) return (SNMP_RET_IGN); pdu_b->asn_len = len; /* now we have the bindings left - construct new message */ resp.error_status = pdu->error_status; resp.error_index = pdu->error_index; resp.type = SNMP_PDU_RESPONSE; code = snmp_pdu_encode_header(resp_b, &resp); if (code != SNMP_CODE_OK) return (SNMP_RET_IGN); if (pdu_b->asn_len > resp_b->asn_len) /* too short */ return (SNMP_RET_IGN); (void)memcpy(resp_b->asn_ptr, pdu_b->asn_cptr, pdu_b->asn_len); resp_b->asn_len -= pdu_b->asn_len; resp_b->asn_ptr += pdu_b->asn_len; code = snmp_fix_encoding(resp_b, &resp); if (code != SNMP_CODE_OK) return (SNMP_RET_IGN); return (SNMP_RET_OK); } static void snmp_debug_func(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); fprintf(stderr, "\n"); } Index: stable/9/contrib/bsnmp/snmp_mibII/mibII_ip.c =================================================================== --- stable/9/contrib/bsnmp/snmp_mibII/mibII_ip.c (revision 301661) +++ stable/9/contrib/bsnmp/snmp_mibII/mibII_ip.c (revision 301662) @@ -1,493 +1,493 @@ /* * Copyright (c) 2001-2003 * Fraunhofer Institute for Open Communication Systems (FhG Fokus). * All rights reserved. * * Author: Harti Brandt * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $Begemot: bsnmp/snmp_mibII/mibII_ip.c,v 1.11 2005/05/23 09:03:40 brandt_h Exp $ * * ip group scalars. */ #include "mibII.h" #include "mibII_oid.h" #include #include #include #include #include static struct ipstat ipstat; static u_int ip_idrop; static struct icmpstat icmpstat; static int ip_forwarding; static int ip_defttl; static uint64_t ip_tick; static uint64_t ipstat_tick; static int fetch_ipstat(void) { size_t len; len = sizeof(ipstat); if (sysctlbyname("net.inet.ip.stats", &ipstat, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.ip.stats: %m"); return (-1); } if (len != sizeof(ipstat)) { syslog(LOG_ERR, "net.inet.ip.stats: wrong size"); return (-1); } len = sizeof(ip_idrop); if (sysctlbyname("net.inet.ip.intr_queue_drops", &ip_idrop, &len, NULL, 0) == -1) syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: %m"); if (len != sizeof(ip_idrop)) { syslog(LOG_WARNING, "net.inet.ip.intr_queue_drops: wrong size"); ip_idrop = 0; } len = sizeof(icmpstat); if (sysctlbyname("net.inet.icmp.stats", &icmpstat, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.icmp.stats: %m"); return (-1); } if (len != sizeof(icmpstat)) { syslog(LOG_ERR, "net.inet.icmp.stats: wrong size"); return (-1); } ipstat_tick = get_ticks(); return (0); } static int fetch_ip(void) { size_t len; len = sizeof(ip_forwarding); if (sysctlbyname("net.inet.ip.forwarding", &ip_forwarding, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.ip.forwarding: %m"); return (-1); } if (len != sizeof(ip_forwarding)) { syslog(LOG_ERR, "net.inet.ip.forwarding: wrong size"); return (-1); } len = sizeof(ip_defttl); if (sysctlbyname("net.inet.ip.ttl", &ip_defttl, &len, NULL, 0) == -1) { syslog(LOG_ERR, "net.inet.ip.ttl: %m"); return (-1); } if (len != sizeof(ip_defttl)) { syslog(LOG_ERR, "net.inet.ip.ttl: wrong size"); return (-1); } ip_tick = get_ticks(); return (0); } static int ip_forward(int forw, int *old) { size_t olen; olen = sizeof(*old); if (sysctlbyname("net.inet.ip.forwarding", old, old ? &olen : NULL, &forw, sizeof(forw)) == -1) { syslog(LOG_ERR, "set net.inet.ip.forwarding: %m"); return (-1); } ip_forwarding = forw; return (0); } static int ip_setttl(int ttl, int *old) { size_t olen; olen = sizeof(*old); if (sysctlbyname("net.inet.ip.ttl", old, old ? &olen : NULL, &ttl, sizeof(ttl)) == -1) { syslog(LOG_ERR, "set net.inet.ip.ttl: %m"); return (-1); } ip_defttl = ttl; return (0); } /* * READ/WRITE ip group. */ int op_ip(struct snmp_context *ctx, struct snmp_value *value, u_int sub, u_int idx __unused, enum snmp_op op) { - int old; + int old = 0; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: if (ip_tick < this_tick) if (fetch_ip() == -1) return (SNMP_ERR_GENERR); switch (value->var.subs[sub - 1]) { case LEAF_ipForwarding: ctx->scratch->int1 = ip_forwarding ? 1 : 2; ctx->scratch->int2 = value->v.integer; if (value->v.integer == 1) { if (!ip_forwarding && ip_forward(1, &old)) return (SNMP_ERR_GENERR); ctx->scratch->int1 = old ? 1 : 2; } else if (value->v.integer == 2) { if (ip_forwarding && ip_forward(0, &old)) return (SNMP_ERR_GENERR); ctx->scratch->int1 = old; } else return (SNMP_ERR_WRONG_VALUE); break; case LEAF_ipDefaultTTL: ctx->scratch->int1 = ip_defttl; ctx->scratch->int2 = value->v.integer; if (value->v.integer < 1 || value->v.integer > 255) return (SNMP_ERR_WRONG_VALUE); if (ip_defttl != value->v.integer && ip_setttl(value->v.integer, &old)) return (SNMP_ERR_GENERR); ctx->scratch->int1 = old; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: switch (value->var.subs[sub - 1]) { case LEAF_ipForwarding: if (ctx->scratch->int1 == 1) { if (ctx->scratch->int2 == 2) (void)ip_forward(1, NULL); } else { if (ctx->scratch->int2 == 1) (void)ip_forward(0, NULL); } break; case LEAF_ipDefaultTTL: if (ctx->scratch->int1 != ctx->scratch->int2) (void)ip_setttl(ctx->scratch->int1, NULL); break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: return (SNMP_ERR_NOERROR); } if (ip_tick < this_tick) if (fetch_ip() == -1) return (SNMP_ERR_GENERR); switch (value->var.subs[sub - 1]) { case LEAF_ipForwarding: value->v.integer = ip_forwarding ? 1 : 2; break; case LEAF_ipDefaultTTL: value->v.integer = ip_defttl; break; } return (SNMP_ERR_NOERROR); } /* * READ-ONLY statistics ip group. */ int op_ipstat(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int idx __unused, enum snmp_op op) { switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } if (ipstat_tick < this_tick) fetch_ipstat(); switch (value->var.subs[sub - 1]) { case LEAF_ipInReceives: value->v.uint32 = ipstat.ips_total; break; case LEAF_ipInHdrErrors: value->v.uint32 = ipstat.ips_badsum + ipstat.ips_tooshort + ipstat.ips_toosmall + ipstat.ips_badhlen + ipstat.ips_badlen + ipstat.ips_badvers + + ipstat.ips_toolong; break; case LEAF_ipInAddrErrors: value->v.uint32 = ipstat.ips_cantforward; break; case LEAF_ipForwDatagrams: value->v.uint32 = ipstat.ips_forward; break; case LEAF_ipInUnknownProtos: value->v.uint32 = ipstat.ips_noproto; break; case LEAF_ipInDiscards: value->v.uint32 = ip_idrop; break; case LEAF_ipInDelivers: value->v.uint32 = ipstat.ips_delivered; break; case LEAF_ipOutRequests: value->v.uint32 = ipstat.ips_localout; break; case LEAF_ipOutDiscards: value->v.uint32 = ipstat.ips_odropped; break; case LEAF_ipOutNoRoutes: value->v.uint32 = ipstat.ips_noroute; break; case LEAF_ipReasmTimeout: value->v.integer = IPFRAGTTL; break; case LEAF_ipReasmReqds: value->v.uint32 = ipstat.ips_fragments; break; case LEAF_ipReasmOKs: value->v.uint32 = ipstat.ips_reassembled; break; case LEAF_ipReasmFails: value->v.uint32 = ipstat.ips_fragdropped + ipstat.ips_fragtimeout; break; case LEAF_ipFragOKs: value->v.uint32 = ipstat.ips_fragmented; break; case LEAF_ipFragFails: value->v.uint32 = ipstat.ips_cantfrag; break; case LEAF_ipFragCreates: value->v.uint32 = ipstat.ips_ofragments; break; } return (SNMP_ERR_NOERROR); } /* * READ-ONLY statistics icmp group. */ int op_icmpstat(struct snmp_context *ctx __unused, struct snmp_value *value, u_int sub, u_int idx __unused, enum snmp_op op) { u_int i; switch (op) { case SNMP_OP_GETNEXT: abort(); case SNMP_OP_GET: break; case SNMP_OP_SET: return (SNMP_ERR_NOT_WRITEABLE); case SNMP_OP_ROLLBACK: case SNMP_OP_COMMIT: abort(); } if (ipstat_tick < this_tick) fetch_ipstat(); switch (value->var.subs[sub - 1]) { case LEAF_icmpInMsgs: value->v.integer = 0; for (i = 0; i <= ICMP_MAXTYPE; i++) value->v.integer += icmpstat.icps_inhist[i]; value->v.integer += icmpstat.icps_tooshort + icmpstat.icps_checksum; /* missing: bad type and packets on faith */ break; case LEAF_icmpInErrors: value->v.integer = icmpstat.icps_tooshort + icmpstat.icps_checksum + icmpstat.icps_badlen + icmpstat.icps_badcode + icmpstat.icps_bmcastecho + icmpstat.icps_bmcasttstamp; break; case LEAF_icmpInDestUnreachs: value->v.integer = icmpstat.icps_inhist[ICMP_UNREACH]; break; case LEAF_icmpInTimeExcds: value->v.integer = icmpstat.icps_inhist[ICMP_TIMXCEED]; break; case LEAF_icmpInParmProbs: value->v.integer = icmpstat.icps_inhist[ICMP_PARAMPROB]; break; case LEAF_icmpInSrcQuenchs: value->v.integer = icmpstat.icps_inhist[ICMP_SOURCEQUENCH]; break; case LEAF_icmpInRedirects: value->v.integer = icmpstat.icps_inhist[ICMP_REDIRECT]; break; case LEAF_icmpInEchos: value->v.integer = icmpstat.icps_inhist[ICMP_ECHO]; break; case LEAF_icmpInEchoReps: value->v.integer = icmpstat.icps_inhist[ICMP_ECHOREPLY]; break; case LEAF_icmpInTimestamps: value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMP]; break; case LEAF_icmpInTimestampReps: value->v.integer = icmpstat.icps_inhist[ICMP_TSTAMPREPLY]; break; case LEAF_icmpInAddrMasks: value->v.integer = icmpstat.icps_inhist[ICMP_MASKREQ]; break; case LEAF_icmpInAddrMaskReps: value->v.integer = icmpstat.icps_inhist[ICMP_MASKREPLY]; break; case LEAF_icmpOutMsgs: value->v.integer = 0; for (i = 0; i <= ICMP_MAXTYPE; i++) value->v.integer += icmpstat.icps_outhist[i]; value->v.integer += icmpstat.icps_badaddr + icmpstat.icps_noroute; break; case LEAF_icmpOutErrors: value->v.integer = icmpstat.icps_badaddr + icmpstat.icps_noroute; break; case LEAF_icmpOutDestUnreachs: value->v.integer = icmpstat.icps_outhist[ICMP_UNREACH]; break; case LEAF_icmpOutTimeExcds: value->v.integer = icmpstat.icps_outhist[ICMP_TIMXCEED]; break; case LEAF_icmpOutParmProbs: value->v.integer = icmpstat.icps_outhist[ICMP_PARAMPROB]; break; case LEAF_icmpOutSrcQuenchs: value->v.integer = icmpstat.icps_outhist[ICMP_SOURCEQUENCH]; break; case LEAF_icmpOutRedirects: value->v.integer = icmpstat.icps_outhist[ICMP_REDIRECT]; break; case LEAF_icmpOutEchos: value->v.integer = icmpstat.icps_outhist[ICMP_ECHO]; break; case LEAF_icmpOutEchoReps: value->v.integer = icmpstat.icps_outhist[ICMP_ECHOREPLY]; break; case LEAF_icmpOutTimestamps: value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMP]; break; case LEAF_icmpOutTimestampReps: value->v.integer = icmpstat.icps_outhist[ICMP_TSTAMPREPLY]; break; case LEAF_icmpOutAddrMasks: value->v.integer = icmpstat.icps_outhist[ICMP_MASKREQ]; break; case LEAF_icmpOutAddrMaskReps: value->v.integer = icmpstat.icps_outhist[ICMP_MASKREPLY]; break; } return (SNMP_ERR_NOERROR); } Index: stable/9/contrib/bsnmp/snmp_mibII =================================================================== --- stable/9/contrib/bsnmp/snmp_mibII (revision 301661) +++ stable/9/contrib/bsnmp/snmp_mibII (revision 301662) Property changes on: stable/9/contrib/bsnmp/snmp_mibII ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /stable/10/contrib/bsnmp/snmp_mibII:r301661 Index: stable/9/contrib/bsnmp/snmp_target/target_snmp.c =================================================================== --- stable/9/contrib/bsnmp/snmp_target/target_snmp.c (revision 301661) +++ stable/9/contrib/bsnmp/snmp_target/target_snmp.c (revision 301662) @@ -1,837 +1,840 @@ /*- * Copyright (c) 2010 The FreeBSD Foundation * All rights reserved. * * This software was developed by Shteryana Sotirova Shopova under * sponsorship from the FreeBSD Foundation. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. * * $FreeBSD$ */ #include #include #include #include #include #include #include #include #include #include "asn1.h" #include "snmp.h" #include "snmpmod.h" #include "target_tree.h" #include "target_oid.h" static struct lmodule *target_module; /* For the registration. */ static const struct asn_oid oid_target = OIDX_snmpTargetMIB; static const struct asn_oid oid_notification = OIDX_snmpNotificationMIB; static uint reg_target; static uint reg_notification; static int32_t target_lock; static const struct asn_oid oid_udp_domain = OIDX_snmpUDPDomain; /* * Internal datastructures and forward declarations. */ static void target_append_index(struct asn_oid *, uint, const char *); static int target_decode_index(const struct asn_oid *, uint, char *); static struct target_address *target_get_address(const struct asn_oid *, uint); static struct target_address *target_get_next_address(const struct asn_oid *, uint); static struct target_param *target_get_param(const struct asn_oid *, uint); static struct target_param *target_get_next_param(const struct asn_oid *, uint); static struct target_notify *target_get_notify(const struct asn_oid *, uint); static struct target_notify *target_get_next_notify(const struct asn_oid *, uint); int op_snmp_target(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { struct snmpd_target_stats *ctx_stats; if (val->var.subs[sub - 1] == LEAF_snmpTargetSpinLock) { switch (op) { case SNMP_OP_GET: if (++target_lock == INT32_MAX) target_lock = 0; val->v.integer = target_lock; break; case SNMP_OP_GETNEXT: abort(); case SNMP_OP_SET: if (val->v.integer != target_lock) return (SNMP_ERR_INCONS_VALUE); break; case SNMP_OP_ROLLBACK: /* FALLTHROUGH */ case SNMP_OP_COMMIT: break; } return (SNMP_ERR_NOERROR); } else if (op == SNMP_OP_SET) return (SNMP_ERR_NOT_WRITEABLE); if ((ctx_stats = bsnmpd_get_target_stats()) == NULL) return (SNMP_ERR_GENERR); if (op == SNMP_OP_GET) { switch (val->var.subs[sub - 1]) { case LEAF_snmpUnavailableContexts: val->v.uint32 = ctx_stats->unavail_contexts; break; case LEAF_snmpUnknownContexts: val->v.uint32 = ctx_stats->unknown_contexts; break; default: return (SNMP_ERR_NOSUCHNAME); } return (SNMP_ERR_NOERROR); } abort(); } int op_snmp_target_addrs(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; switch (op) { case SNMP_OP_GET: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((addrs = target_get_next_address(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, addrs->name); break; case SNMP_OP_SET: if ((addrs = target_get_address(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetAddrRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_NOSUCHNAME); if (addrs != NULL) { if (community != COMM_INITIALIZE && addrs->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (addrs->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTDomain: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetAddrTAddress: if (val->v.octetstring.len != SNMP_UDP_ADDR_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->ptr1 = malloc(SNMP_UDP_ADDR_SIZ); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); memcpy(ctx->scratch->ptr1, addrs->address, SNMP_UDP_ADDR_SIZ); memcpy(addrs->address, val->v.octetstring.octets, SNMP_UDP_ADDR_SIZ); break; case LEAF_snmpTargetAddrTagList: if (val->v.octetstring.len >= SNMP_TAG_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(addrs->taglist) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, addrs->taglist, ctx->scratch->int1); memcpy(addrs->taglist, val->v.octetstring.octets, val->v.octetstring.len); addrs->taglist[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetAddrParams: if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(addrs->paramname) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, addrs->paramname, ctx->scratch->int1); memcpy(addrs->paramname, val->v.octetstring.octets, val->v.octetstring.len); addrs->paramname[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetAddrRetryCount: ctx->scratch->int1 = addrs->retry; addrs->retry = val->v.integer; break; case LEAF_snmpTargetAddrTimeout: ctx->scratch->int1 = addrs->timeout; addrs->timeout = val->v.integer / 10; break; case LEAF_snmpTargetAddrStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetAddrRowStatus: if (addrs != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); if (val->v.integer == RowStatus_active && (addrs->address[0] == 0 || strlen(addrs->taglist) == 0 || strlen(addrs->paramname) == 0)) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = addrs->status; addrs->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndWait || target_decode_index(&val->var, sub, aname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((addrs = target_new_address(aname)) == NULL) return (SNMP_ERR_GENERR); addrs->status = RowStatus_destroy; if (community != COMM_INITIALIZE) addrs->type = StorageType_volatile; else addrs->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTAddress: case LEAF_snmpTargetAddrTagList: case LEAF_snmpTargetAddrParams: free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrRowStatus: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_address(addrs)); else if (val->v.integer == RowStatus_active) return (target_activate_address(addrs)); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((addrs = target_get_address(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTAddress: memcpy(addrs->address, ctx->scratch->ptr1, SNMP_UDP_ADDR_SIZ); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrTagList: strlcpy(addrs->taglist, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrParams: strlcpy(addrs->paramname, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetAddrRetryCount: addrs->retry = ctx->scratch->int1; break; case LEAF_snmpTargetAddrTimeout: addrs->timeout = ctx->scratch->int1; break; case LEAF_snmpTargetAddrRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_address(addrs)); break; default: break; } + return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetAddrTDomain: return (oid_get(val, &oid_udp_domain)); case LEAF_snmpTargetAddrTAddress: return (string_get(val, addrs->address, SNMP_UDP_ADDR_SIZ)); case LEAF_snmpTargetAddrTimeout: val->v.integer = addrs->timeout; break; case LEAF_snmpTargetAddrRetryCount: val->v.integer = addrs->retry; break; case LEAF_snmpTargetAddrTagList: return (string_get(val, addrs->taglist, -1)); case LEAF_snmpTargetAddrParams: return (string_get(val, addrs->paramname, -1)); case LEAF_snmpTargetAddrStorageType: val->v.integer = addrs->type; break; case LEAF_snmpTargetAddrRowStatus: val->v.integer = addrs->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_snmp_target_params(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; switch (op) { case SNMP_OP_GET: if ((param = target_get_param(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((param = target_get_next_param(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, param->name); break; case SNMP_OP_SET: if ((param = target_get_param(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_NOSUCHNAME); if (param != NULL) { if (community != COMM_INITIALIZE && param->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); if (param->status == RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: if (val->v.integer != SNMP_MPM_SNMP_V1 && val->v.integer != SNMP_MPM_SNMP_V2c && val->v.integer != SNMP_MPM_SNMP_V3) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->mpmodel; param->mpmodel = val->v.integer; break; case LEAF_snmpTargetParamsSecurityModel: if (val->v.integer != SNMP_SECMODEL_SNMPv1 && val->v.integer != SNMP_SECMODEL_SNMPv2c && val->v.integer != SNMP_SECMODEL_USM) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->sec_model; param->sec_model = val->v.integer; break; case LEAF_snmpTargetParamsSecurityName: if (val->v.octetstring.len >= SNMP_ADM_STR32_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(param->secname) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, param->secname, ctx->scratch->int1); memcpy(param->secname, val->v.octetstring.octets, val->v.octetstring.len); param->secname[val->v.octetstring.len] = '\0'; break; case LEAF_snmpTargetParamsSecurityLevel: if (val->v.integer != SNMP_noAuthNoPriv && val->v.integer != SNMP_authNoPriv && val->v.integer != SNMP_authPriv) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->sec_level; param->sec_level = val->v.integer; break; case LEAF_snmpTargetParamsStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpTargetParamsRowStatus: if (param != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); if (val->v.integer == RowStatus_active && (param->sec_model == 0 || param->sec_level == 0 || strlen(param->secname) == 0)) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = param->status; param->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndWait || target_decode_index(&val->var, sub, pname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((param = target_new_param(pname)) == NULL) return (SNMP_ERR_GENERR); param->status = RowStatus_destroy; if (community != COMM_INITIALIZE) param->type = StorageType_volatile; else param->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsSecurityName: free(ctx->scratch->ptr1); break; case LEAF_snmpTargetParamsRowStatus: if ((param = target_get_param(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_param(param)); break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((param = target_get_param(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpTargetParamsRowStatus || val->v.integer != RowStatus_createAndWait)) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: param->mpmodel = ctx->scratch->int1; break; case LEAF_snmpTargetParamsSecurityModel: param->sec_model = ctx->scratch->int1; break; case LEAF_snmpTargetParamsSecurityName: strlcpy(param->secname, ctx->scratch->ptr1, sizeof(param->secname)); free(ctx->scratch->ptr1); break; case LEAF_snmpTargetParamsSecurityLevel: param->sec_level = ctx->scratch->int1; break; case LEAF_snmpTargetParamsRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_param(param)); break; default: break; } return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpTargetParamsMPModel: val->v.integer = param->mpmodel; break; case LEAF_snmpTargetParamsSecurityModel: val->v.integer = param->sec_model; break; case LEAF_snmpTargetParamsSecurityName: return (string_get(val, param->secname, -1)); case LEAF_snmpTargetParamsSecurityLevel: val->v.integer = param->sec_level; break; case LEAF_snmpTargetParamsStorageType: val->v.integer = param->type; break; case LEAF_snmpTargetParamsRowStatus: val->v.integer = param->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } int op_snmp_notify(struct snmp_context *ctx __unused, struct snmp_value *val, uint32_t sub, uint32_t iidx __unused, enum snmp_op op) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; switch (op) { case SNMP_OP_GET: if ((notify = target_get_notify(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); break; case SNMP_OP_GETNEXT: if ((notify = target_get_next_notify(&val->var, sub)) == NULL) return (SNMP_ERR_NOSUCHNAME); target_append_index(&val->var, sub, notify->name); break; case SNMP_OP_SET: if ((notify = target_get_notify(&val->var, sub)) == NULL && (val->var.subs[sub - 1] != LEAF_snmpNotifyRowStatus || val->v.integer != RowStatus_createAndGo)) return (SNMP_ERR_NOSUCHNAME); if (notify != NULL) { if (community != COMM_INITIALIZE && notify->type == StorageType_readOnly) return (SNMP_ERR_NOT_WRITEABLE); } switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: if (val->v.octetstring.len >= SNMP_TAG_SIZ) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = strlen(notify->taglist) + 1; ctx->scratch->ptr1 = malloc(ctx->scratch->int1); if (ctx->scratch->ptr1 == NULL) return (SNMP_ERR_GENERR); strlcpy(ctx->scratch->ptr1, notify->taglist, ctx->scratch->int1); memcpy(notify->taglist, val->v.octetstring.octets, val->v.octetstring.len); notify->taglist[val->v.octetstring.len] = '\0'; break; case LEAF_snmpNotifyType: /* FALLTHROUGH */ case LEAF_snmpNotifyStorageType: return (SNMP_ERR_INCONS_VALUE); case LEAF_snmpNotifyRowStatus: if (notify != NULL) { if (val->v.integer != RowStatus_active && val->v.integer != RowStatus_destroy) return (SNMP_ERR_INCONS_VALUE); ctx->scratch->int1 = notify->status; notify->status = val->v.integer; return (SNMP_ERR_NOERROR); } if (val->v.integer != RowStatus_createAndGo || target_decode_index(&val->var, sub, nname) < 0) return (SNMP_ERR_INCONS_VALUE); if ((notify = target_new_notify(nname)) == NULL) return (SNMP_ERR_GENERR); notify->status = RowStatus_destroy; if (community != COMM_INITIALIZE) notify->type = StorageType_volatile; else notify->type = StorageType_readOnly; break; } return (SNMP_ERR_NOERROR); case SNMP_OP_COMMIT: switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: free(ctx->scratch->ptr1); break; case LEAF_snmpNotifyRowStatus: notify = target_get_notify(&val->var, sub); if (notify == NULL) return (SNMP_ERR_GENERR); if (val->v.integer == RowStatus_destroy) return (target_delete_notify(notify)); else notify->status = RowStatus_active; break; default: break; } return (SNMP_ERR_NOERROR); case SNMP_OP_ROLLBACK: if ((notify = target_get_notify(&val->var, sub)) == NULL) return (SNMP_ERR_GENERR); switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: strlcpy(notify->taglist, ctx->scratch->ptr1, ctx->scratch->int1); free(ctx->scratch->ptr1); break; case LEAF_snmpNotifyRowStatus: if (ctx->scratch->int1 == RowStatus_destroy) return (target_delete_notify(notify)); break; default: break; } + return (SNMP_ERR_NOERROR); default: abort(); } switch (val->var.subs[sub - 1]) { case LEAF_snmpNotifyTag: return (string_get(val, notify->taglist, -1)); case LEAF_snmpNotifyType: val->v.integer = snmpNotifyType_trap; break; case LEAF_snmpNotifyStorageType: val->v.integer = notify->type; break; case LEAF_snmpNotifyRowStatus: val->v.integer = notify->status; break; default: abort(); } return (SNMP_ERR_NOERROR); } static void target_append_index(struct asn_oid *oid, uint sub, const char *name) { uint32_t i; oid->len = sub + strlen(name); for (i = 0; i < strlen(name); i++) oid->subs[sub + i] = name[i]; } static int target_decode_index(const struct asn_oid *oid, uint sub, char *name) { - uint32_t i, len; + uint32_t i; - if ((len = oid->len - sub) >= SNMP_ADM_STR32_SIZ) + if (oid->len - sub != oid->subs[sub] + 1 || oid->subs[sub] >= + SNMP_ADM_STR32_SIZ) return (-1); - for (i = 0; i < len; i++) - name[i] = oid->subs[sub + i]; + for (i = 0; i < oid->subs[sub]; i++) + name[i] = oid->subs[sub + i + 1]; name[i] = '\0'; return (0); } static struct target_address * target_get_address(const struct asn_oid *oid, uint sub) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; if (target_decode_index(oid, sub, aname) < 0) return (NULL); for (addrs = target_first_address(); addrs != NULL; addrs = target_next_address(addrs)) if (strcmp(aname, addrs->name) == 0) return (addrs); return (NULL); } static struct target_address * target_get_next_address(const struct asn_oid * oid, uint sub) { char aname[SNMP_ADM_STR32_SIZ]; struct target_address *addrs; if (oid->len - sub == 0) return (target_first_address()); if (target_decode_index(oid, sub, aname) < 0) return (NULL); for (addrs = target_first_address(); addrs != NULL; addrs = target_next_address(addrs)) if (strcmp(aname, addrs->name) == 0) return (target_next_address(addrs)); return (NULL); } static struct target_param * target_get_param(const struct asn_oid *oid, uint sub) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; if (target_decode_index(oid, sub, pname) < 0) return (NULL); for (param = target_first_param(); param != NULL; param = target_next_param(param)) if (strcmp(pname, param->name) == 0) return (param); return (NULL); } static struct target_param * target_get_next_param(const struct asn_oid *oid, uint sub) { char pname[SNMP_ADM_STR32_SIZ]; struct target_param *param; if (oid->len - sub == 0) return (target_first_param()); if (target_decode_index(oid, sub, pname) < 0) return (NULL); for (param = target_first_param(); param != NULL; param = target_next_param(param)) if (strcmp(pname, param->name) == 0) return (target_next_param(param)); return (NULL); } static struct target_notify * target_get_notify(const struct asn_oid *oid, uint sub) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; if (target_decode_index(oid, sub, nname) < 0) return (NULL); for (notify = target_first_notify(); notify != NULL; notify = target_next_notify(notify)) if (strcmp(nname, notify->name) == 0) return (notify); return (NULL); } static struct target_notify * target_get_next_notify(const struct asn_oid *oid, uint sub) { char nname[SNMP_ADM_STR32_SIZ]; struct target_notify *notify; if (oid->len - sub == 0) return (target_first_notify()); if (target_decode_index(oid, sub, nname) < 0) return (NULL); for (notify = target_first_notify(); notify != NULL; notify = target_next_notify(notify)) if (strcmp(nname, notify->name) == 0) return (target_next_notify(notify)); return (NULL); } static int target_init(struct lmodule *mod, int argc __unused, char *argv[] __unused) { target_module = mod; target_lock = random(); return (0); } static int target_fini(void) { target_flush_all(); or_unregister(reg_target); or_unregister(reg_notification); return (0); } static void target_start(void) { reg_target = or_register(&oid_target, "The MIB module for managing SNMP Management Targets.", target_module); reg_notification = or_register(&oid_notification, "The MIB module for configuring generation of SNMP notifications.", target_module); } static void target_dump(void) { /* XXX: dump the module stats & list of mgmt targets */ } const char target_comment[] = \ "This module implements SNMP Management Target MIB Module defined in RFC 3413."; const struct snmp_module config = { .comment = target_comment, .init = target_init, .fini = target_fini, .start = target_start, .tree = target_ctree, .dump = target_dump, .tree_size = target_CTREE_SIZE, }; Index: stable/9/contrib/bsnmp =================================================================== --- stable/9/contrib/bsnmp (revision 301661) +++ stable/9/contrib/bsnmp (revision 301662) Property changes on: stable/9/contrib/bsnmp ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /stable/10/contrib/bsnmp:r301661 Index: stable/9/contrib =================================================================== --- stable/9/contrib (revision 301661) +++ stable/9/contrib (revision 301662) Property changes on: stable/9/contrib ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /stable/10/contrib:r301661 Index: stable/9 =================================================================== --- stable/9 (revision 301661) +++ stable/9 (revision 301662) Property changes on: stable/9 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,2 ## Merged /head:r256678,256680,260986,272878,286402 Merged /stable/10:r301661