Annotation of src/usr.bin/snmp/usm.c, Revision 1.1
1.1 ! martijn 1: /* $OpenBSD: usm.c,v 1.16 2019/06/11 05:36:32 martijn Exp $ */
! 2:
! 3: /*
! 4: * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
! 5: *
! 6: * Permission to use, copy, modify, and distribute this software for any
! 7: * purpose with or without fee is hereby granted, provided that the above
! 8: * copyright notice and this permission notice appear in all copies.
! 9: *
! 10: * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
! 11: * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
! 12: * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
! 13: * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
! 14: * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
! 15: * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
! 16: * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
! 17: */
! 18:
! 19: #include <sys/time.h>
! 20:
! 21: #include <openssl/evp.h>
! 22: #include <openssl/hmac.h>
! 23:
! 24: #include <ber.h>
! 25: #include <errno.h>
! 26: #include <string.h>
! 27: #include <time.h>
! 28:
! 29: #include "smi.h"
! 30: #include "snmp.h"
! 31: #include "usm.h"
! 32:
! 33: #define USM_MAX_DIGESTLEN 48
! 34: #define USM_MAX_TIMEWINDOW 150
! 35: #define USM_SALTOFFSET 8
! 36:
! 37: struct usm_sec {
! 38: struct snmp_sec snmp;
! 39: char *user;
! 40: size_t userlen;
! 41: int engineidset;
! 42: char *engineid;
! 43: size_t engineidlen;
! 44: int bootsset;
! 45: uint32_t boots;
! 46: int timeset;
! 47: uint32_t time;
! 48: struct timespec timecheck;
! 49: };
! 50:
! 51: static int usm_doinit(struct snmp_agent *);
! 52: static char *usm_genparams(struct snmp_agent *, size_t *);
! 53: static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,
! 54: size_t, uint8_t);
! 55: static void usm_free(void *);
! 56:
! 57: struct snmp_sec *
! 58: usm_init(const char *user, size_t userlen)
! 59: {
! 60: struct snmp_sec *sec;
! 61: struct usm_sec *usm;
! 62:
! 63: if (user == NULL || user[0] == '\0') {
! 64: errno = EINVAL;
! 65: return NULL;
! 66: }
! 67:
! 68: if ((sec = malloc(sizeof(*sec))) == NULL)
! 69: return NULL;
! 70:
! 71: if ((usm = calloc(1, sizeof(struct usm_sec))) == NULL) {
! 72: free(sec);
! 73: return NULL;
! 74: }
! 75: if ((usm->user = malloc(userlen)) == NULL) {
! 76: free(sec);
! 77: free(usm);
! 78: return NULL;
! 79: }
! 80: memcpy(usm->user, user, userlen);
! 81: usm->userlen = userlen;
! 82:
! 83: sec->model = SNMP_SEC_USM;
! 84: sec->init = usm_doinit;
! 85: sec->genparams = usm_genparams;
! 86: sec->parseparams = usm_parseparams;
! 87: sec->free = usm_free;
! 88: sec->data = usm;
! 89: return sec;
! 90: }
! 91:
! 92: static int
! 93: usm_doinit(struct snmp_agent *agent)
! 94: {
! 95: struct ber_element *ber;
! 96: struct usm_sec *usm = agent->v3->sec->data;
! 97: int level;
! 98: size_t userlen;
! 99:
! 100: if (usm->engineidset && usm->bootsset && usm->timeset)
! 101: return 0;
! 102:
! 103: level = agent->v3->level;
! 104: agent->v3->level = SNMP_MSGFLAG_REPORT;
! 105: userlen = usm->userlen;
! 106: usm->userlen = 0;
! 107:
! 108: if ((ber = snmp_get(agent, NULL, 0)) == NULL) {
! 109: agent->v3->level = level;
! 110: usm->userlen = userlen;
! 111: return -1;
! 112: }
! 113: ber_free_element(ber);
! 114:
! 115: agent->v3->level = level;
! 116: usm->userlen = userlen;
! 117:
! 118: return 0;
! 119: }
! 120:
! 121: static char *
! 122: usm_genparams(struct snmp_agent *agent, size_t *len)
! 123: {
! 124: struct ber ber;
! 125: struct ber_element *params;
! 126: struct usm_sec *usm = agent->v3->sec->data;
! 127: char *secparams = NULL;
! 128: ssize_t berlen = 0;
! 129: struct timespec now, timediff;
! 130: uint32_t boots, time;
! 131:
! 132: if (usm->timeset) {
! 133: if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
! 134: return NULL;
! 135: timespecsub(&now, &(usm->timecheck), &timediff);
! 136: time = usm->time + timediff.tv_sec;
! 137: } else
! 138: time = 0;
! 139: boots = usm->boots;
! 140:
! 141: if ((params = ber_printf_elements(NULL, "{xddxxx}", usm->engineid,
! 142: usm->engineidlen, boots, time, usm->user, usm->userlen, NULL,
! 143: (size_t) 0, NULL, (size_t) 0)) == NULL)
! 144: return NULL;
! 145:
! 146: bzero(&ber, sizeof(ber));
! 147: ber_set_application(&ber, smi_application);
! 148: if (ber_write_elements(&ber, params) != -1)
! 149: berlen = ber_copy_writebuf(&ber, (void **)&secparams);
! 150:
! 151: *len = berlen;
! 152: ber_free_element(params);
! 153: ber_free(&ber);
! 154: return secparams;
! 155: }
! 156:
! 157: static int
! 158: usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
! 159: off_t secparamsoffset, char *buf, size_t buflen, uint8_t level)
! 160: {
! 161: struct usm_sec *usm = agent->v3->sec->data;
! 162: struct ber ber;
! 163: struct ber_element *secparams;
! 164: char *engineid, *user;
! 165: size_t engineidlen, userlen;
! 166: struct timespec now, timediff;
! 167: uint32_t boots, time;
! 168:
! 169: bzero(&ber, sizeof(ber));
! 170:
! 171: ber_set_application(&ber, smi_application);
! 172: ber_set_readbuf(&ber, buf, buflen);
! 173: if ((secparams = ber_read_elements(&ber, NULL)) == NULL)
! 174: return -1;
! 175: ber_free(&ber);
! 176:
! 177: if (ber_scanf_elements(secparams, "{xddxSS}", &engineid, &engineidlen,
! 178: &boots, &time, &user, &userlen) == -1)
! 179: goto fail;
! 180:
! 181: if (!usm->engineidset) {
! 182: if (usm_setengineid(agent->v3->sec, engineid,
! 183: engineidlen) == -1)
! 184: goto fail;
! 185: } else {
! 186: if (usm->engineidlen != engineidlen)
! 187: goto fail;
! 188: if (memcmp(usm->engineid, engineid, engineidlen) != 0)
! 189: goto fail;
! 190: }
! 191:
! 192: if (!usm->bootsset) {
! 193: usm->boots = boots;
! 194: usm->bootsset = 1;
! 195: } else {
! 196: if (boots < usm->boots)
! 197: goto fail;
! 198: if (boots > usm->boots) {
! 199: usm->bootsset = 0;
! 200: usm->timeset = 0;
! 201: usm_doinit(agent);
! 202: goto fail;
! 203: }
! 204: }
! 205:
! 206: if (!usm->timeset) {
! 207: usm->time = time;
! 208: if (clock_gettime(CLOCK_MONOTONIC, &usm->timecheck) == -1)
! 209: goto fail;
! 210: usm->timeset = 1;
! 211: } else {
! 212: if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)
! 213: goto fail;
! 214: timespecsub(&now, &(usm->timecheck), &timediff);
! 215: if (time < usm->time + timediff.tv_sec - USM_MAX_TIMEWINDOW ||
! 216: time > usm->time + timediff.tv_sec + USM_MAX_TIMEWINDOW) {
! 217: usm->bootsset = 0;
! 218: usm->timeset = 0;
! 219: usm_doinit(agent);
! 220: goto fail;
! 221: }
! 222: }
! 223:
! 224: if (userlen != usm->userlen ||
! 225: memcmp(user, usm->user, userlen) != 0)
! 226: goto fail;
! 227:
! 228: ber_free_element(secparams);
! 229: return 0;
! 230:
! 231: fail:
! 232: ber_free_element(secparams);
! 233: return -1;
! 234: }
! 235:
! 236: static void
! 237: usm_free(void *data)
! 238: {
! 239: struct usm_sec *usm = data;
! 240:
! 241: free(usm->user);
! 242: free(usm->engineid);
! 243: free(usm);
! 244: }
! 245:
! 246: int
! 247: usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
! 248: {
! 249: struct usm_sec *usm = sec->data;
! 250:
! 251: if (usm->engineid != NULL)
! 252: free(usm->engineid);
! 253: if ((usm->engineid = malloc(engineidlen)) == NULL)
! 254: return -1;
! 255: memcpy(usm->engineid, engineid, engineidlen);
! 256: usm->engineidlen = engineidlen;
! 257: usm->engineidset = 1;
! 258:
! 259: return 0;
! 260: }
! 261:
! 262: int
! 263: usm_setbootstime(struct snmp_sec *sec, uint32_t boots, uint32_t time)
! 264: {
! 265: struct usm_sec *usm = sec->data;
! 266:
! 267: if (clock_gettime(CLOCK_MONOTONIC, &(usm->timecheck)) == -1)
! 268: return -1;
! 269:
! 270: usm->boots = boots;
! 271: usm->bootsset = 1;
! 272: usm->time = time;
! 273: usm->timeset = 1;
! 274: return 0;
! 275: }