[BACK]Return to snmp.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / snmp

Annotation of src/usr.bin/snmp/snmp.c, Revision 1.4

1.4     ! martijn     1: /*     $OpenBSD: snmp.c,v 1.3 2019/09/18 09:44:38 martijn Exp $        */
1.1       martijn     2:
                      3: /*
                      4:  * Copyright (c) 2019 Martijn van Duren <martijn@openbsd.org>
                      5:  * Copyright (c) 2013 Reyk Floeter <reyk@openbsd.org>
                      6:  *
                      7:  * Permission to use, copy, modify, and distribute this software for any
                      8:  * purpose with or without fee is hereby granted, provided that the above
                      9:  * copyright notice and this permission notice appear in all copies.
                     10:  *
                     11:  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
                     12:  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
                     13:  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
                     14:  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
                     15:  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
                     16:  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
                     17:  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
                     18:  */
                     19:
                     20: #include <sys/socket.h>
                     21:
                     22: #include <errno.h>
                     23: #include <poll.h>
                     24: #include <stdlib.h>
                     25: #include <string.h>
                     26: #include <stdio.h>
                     27: #include <time.h>
                     28:
                     29: #include "ber.h"
                     30: #include "smi.h"
                     31: #include "snmp.h"
                     32:
1.4     ! martijn    33: #define UDP_MAXPACKET 65535
        !            34:
1.1       martijn    35: static struct ber_element *
                     36:     snmp_resolve(struct snmp_agent *, struct ber_element *, int);
1.3       martijn    37: static char *
                     38:     snmp_package(struct snmp_agent *, struct ber_element *, size_t *);
                     39: static struct ber_element *
                     40:     snmp_unpackage(struct snmp_agent *, char *, size_t);
1.4     ! martijn    41: static void snmp_v3_free(struct snmp_v3 *);
        !            42:
        !            43: struct snmp_v3 *
        !            44: snmp_v3_init(int level, const char *ctxname, size_t ctxnamelen,
        !            45:     struct snmp_sec *sec)
        !            46: {
        !            47:        struct snmp_v3 *v3;
        !            48:
        !            49:        if ((level & (SNMP_MSGFLAG_SECMASK | SNMP_MSGFLAG_REPORT)) != level ||
        !            50:            sec == NULL) {
        !            51:                errno = EINVAL;
        !            52:                return NULL;
        !            53:        }
        !            54:        if ((v3 = calloc(1, sizeof(*v3))) == NULL)
        !            55:                return NULL;
        !            56:
        !            57:        v3->level = level | SNMP_MSGFLAG_REPORT;
        !            58:        v3->ctxnamelen = ctxnamelen;
        !            59:        if (ctxnamelen != 0) {
        !            60:                if ((v3->ctxname = malloc(ctxnamelen)) == NULL) {
        !            61:                        free(v3);
        !            62:                        return NULL;
        !            63:                }
        !            64:                memcpy(v3->ctxname, ctxname, ctxnamelen);
        !            65:        }
        !            66:        v3->sec = sec;
        !            67:        return v3;
        !            68: }
        !            69:
        !            70: int
        !            71: snmp_v3_setengineid(struct snmp_v3 *v3, char *engineid, size_t engineidlen)
        !            72: {
        !            73:        if (v3->engineidset)
        !            74:                free(v3->engineid);
        !            75:        if ((v3->engineid = malloc(engineidlen)) == NULL)
        !            76:                return -1;
        !            77:        memcpy(v3->engineid, engineid, engineidlen);
        !            78:        v3->engineidlen = engineidlen;
        !            79:        v3->engineidset = 1;
        !            80:        return 0;
        !            81: }
1.1       martijn    82:
                     83: struct snmp_agent *
                     84: snmp_connect_v12(int fd, enum snmp_version version, const char *community)
                     85: {
                     86:        struct snmp_agent *agent;
                     87:
                     88:        if (version != SNMP_V1 && version != SNMP_V2C) {
                     89:                errno = EINVAL;
                     90:                return NULL;
                     91:        }
                     92:        if ((agent = malloc(sizeof(*agent))) == NULL)
                     93:                return NULL;
                     94:        agent->fd = fd;
                     95:        agent->version = version;
                     96:        if ((agent->community = strdup(community)) == NULL)
                     97:                goto fail;
                     98:        agent->timeout = 1;
                     99:        agent->retries = 5;
1.4     ! martijn   100:        agent->v3 = NULL;
1.1       martijn   101:        return agent;
                    102:
                    103: fail:
                    104:        free(agent);
                    105:        return NULL;
                    106: }
                    107:
1.4     ! martijn   108: struct snmp_agent *
        !           109: snmp_connect_v3(int fd, struct snmp_v3 *v3)
        !           110: {
        !           111:        struct snmp_agent *agent;
        !           112:
        !           113:        if ((agent = malloc(sizeof(*agent))) == NULL)
        !           114:                return NULL;
        !           115:        agent->fd = fd;
        !           116:        agent->version = SNMP_V3;
        !           117:        agent->v3 = v3;
        !           118:        agent->timeout = 1;
        !           119:        agent->retries = 5;
        !           120:        agent->community = NULL;
        !           121:
        !           122:        if (v3->sec->init(agent) == -1) {
        !           123:                snmp_free_agent(agent);
        !           124:                return NULL;
        !           125:        }
        !           126:        return agent;
        !           127: }
        !           128:
1.1       martijn   129: void
                    130: snmp_free_agent(struct snmp_agent *agent)
                    131: {
                    132:        free(agent->community);
1.4     ! martijn   133:        if (agent->v3 != NULL)
        !           134:                snmp_v3_free(agent->v3);
1.1       martijn   135:        free(agent);
                    136: }
                    137:
1.4     ! martijn   138: static void
        !           139: snmp_v3_free(struct snmp_v3 *v3)
        !           140: {
        !           141:        v3->sec->free(v3->sec->data);
        !           142:        free(v3->sec);
        !           143:        free(v3->ctxname);
        !           144:        free(v3->engineid);
        !           145:        free(v3);
        !           146: }
        !           147:
1.1       martijn   148: struct ber_element *
                    149: snmp_get(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
                    150: {
                    151:        struct ber_element *pdu, *varbind;
                    152:        size_t i;
                    153:
                    154:        if ((pdu = ber_add_sequence(NULL)) == NULL)
                    155:                return NULL;
                    156:        if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
                    157:            SNMP_C_GETREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
                    158:                goto fail;
                    159:        for (i = 0; i < len; i++)
                    160:                varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
                    161:                if (varbind == NULL)
                    162:                        goto fail;
                    163:
                    164:        return snmp_resolve(agent, pdu, 1);
                    165: fail:
                    166:        ber_free_elements(pdu);
                    167:        return NULL;
                    168: }
                    169:
                    170: struct ber_element *
                    171: snmp_getnext(struct snmp_agent *agent, struct ber_oid *oid, size_t len)
                    172: {
                    173:        struct ber_element *pdu, *varbind;
                    174:        size_t i;
                    175:
                    176:        if ((pdu = ber_add_sequence(NULL)) == NULL)
                    177:                return NULL;
                    178:        if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
                    179:            SNMP_C_GETNEXTREQ, arc4random() & 0x7fffffff, 0, 0)) == NULL)
                    180:                goto fail;
                    181:        for (i = 0; i < len; i++)
                    182:                varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
                    183:                if (varbind == NULL)
                    184:                        goto fail;
                    185:
                    186:        return snmp_resolve(agent, pdu, 1);
                    187: fail:
                    188:        ber_free_elements(pdu);
                    189:        return NULL;
                    190: }
                    191:
                    192: int
                    193: snmp_trap(struct snmp_agent *agent, struct timespec *uptime,
                    194:     struct ber_oid *oid, struct ber_element *custvarbind)
                    195: {
                    196:        struct ber_element *pdu, *varbind;
                    197:        struct ber_oid sysuptime, trap;
                    198:        long long ticks;
                    199:
                    200:        if ((pdu = ber_add_sequence(NULL)) == NULL)
                    201:                return -1;
                    202:        if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
                    203:            SNMP_C_TRAPV2, arc4random() & 0x7fffffff, 0, 0)) == NULL)
                    204:                goto fail;
                    205:
                    206:        ticks = uptime->tv_sec * 100;
                    207:        ticks += uptime->tv_nsec / 10000000;
                    208:        if (smi_string2oid("sysUpTime.0", &sysuptime) == -1)
                    209:                goto fail;
                    210:        if ((varbind = ber_printf_elements(varbind, "{Oit}", &sysuptime, ticks,
                    211:            BER_CLASS_APPLICATION, SNMP_T_TIMETICKS)) == NULL)
                    212:                goto fail;
                    213:        if (smi_string2oid("snmpTrapOID.0", &trap) == -1)
                    214:                goto fail;
                    215:        if ((varbind = ber_printf_elements(varbind, "{OO}", &trap, oid)) == NULL)
                    216:                goto fail;
                    217:        if (custvarbind != NULL)
                    218:                ber_link_elements(varbind, custvarbind);
                    219:
                    220:        snmp_resolve(agent, pdu, 0);
                    221:        return 0;
                    222: fail:
                    223:        ber_free_elements(pdu);
                    224:        return -1;
                    225: }
                    226:
                    227: struct ber_element *
                    228: snmp_getbulk(struct snmp_agent *agent, struct ber_oid *oid, size_t len,
                    229:     int non_repeaters, int max_repetitions)
                    230: {
                    231:        struct ber_element *pdu, *varbind;
                    232:        size_t i;
                    233:
                    234:        if ((pdu = ber_add_sequence(NULL)) == NULL)
                    235:                return NULL;
                    236:        if ((varbind = ber_printf_elements(pdu, "tddd{", BER_CLASS_CONTEXT,
                    237:            SNMP_C_GETBULKREQ, arc4random() & 0x7fffffff, non_repeaters,
                    238:            max_repetitions)) == NULL)
                    239:                goto fail;
                    240:        for (i = 0; i < len; i++)
                    241:                varbind = ber_printf_elements(varbind, "{O0}", &oid[i]);
                    242:                if (varbind == NULL)
                    243:                        goto fail;
                    244:
                    245:        return snmp_resolve(agent, pdu, 1);
                    246: fail:
                    247:        ber_free_elements(pdu);
                    248:        return NULL;
                    249: }
                    250:
                    251: static struct ber_element *
                    252: snmp_resolve(struct snmp_agent *agent, struct ber_element *pdu, int reply)
                    253: {
1.3       martijn   254:        struct ber_element *varbind;
1.1       martijn   255:        struct ber_oid oid;
                    256:        struct timespec start, now;
                    257:        struct pollfd pfd;
1.3       martijn   258:        char *message;
1.1       martijn   259:        ssize_t len;
                    260:        long long reqid, rreqid;
                    261:        short direction;
                    262:        int to, nfds, ret;
                    263:        int tries;
                    264:        char buf[READ_BUF_SIZE];
                    265:
                    266:        if (ber_scanf_elements(pdu, "{i", &reqid) != 0) {
                    267:                errno = EINVAL;
                    268:                ber_free_elements(pdu);
                    269:                return NULL;
                    270:        }
                    271:
1.3       martijn   272:        if ((message = snmp_package(agent, pdu, &len)) == NULL)
1.1       martijn   273:                return NULL;
                    274:
                    275:        clock_gettime(CLOCK_MONOTONIC, &start);
                    276:        memcpy(&now, &start, sizeof(now));
                    277:        direction = POLLOUT;
                    278:        tries = agent->retries + 1;
                    279:        while (tries) {
                    280:                pfd.fd = agent->fd;
                    281:                pfd.events = direction;
                    282:                if (agent->timeout > 0) {
                    283:                        to = (agent->timeout - (now.tv_sec - start.tv_sec)) * 1000;
                    284:                        to -= (now.tv_nsec - start.tv_nsec) / 1000000;
                    285:                } else
                    286:                        to = INFTIM;
                    287:                nfds = poll(&pfd, 1, to);
                    288:                if (nfds == 0) {
                    289:                        errno = ETIMEDOUT;
                    290:                        direction = POLLOUT;
                    291:                        tries--;
                    292:                        continue;
                    293:                }
                    294:                if (nfds == -1) {
                    295:                        if (errno == EINTR)
                    296:                                continue;
                    297:                        else
                    298:                                goto fail;
                    299:                }
                    300:                if (direction == POLLOUT) {
1.3       martijn   301:                        ret = send(agent->fd, message, len, MSG_DONTWAIT);
1.1       martijn   302:                        if (ret == -1)
                    303:                                goto fail;
                    304:                        if (ret < len) {
                    305:                                errno = EBADMSG;
                    306:                                goto fail;
                    307:                        }
                    308:                        if (!reply)
                    309:                                return NULL;
                    310:                        direction = POLLIN;
                    311:                        continue;
                    312:                }
                    313:                ret = recv(agent->fd, buf, sizeof(buf), MSG_DONTWAIT);
                    314:                if (ret == 0)
                    315:                        errno = ECONNRESET;
                    316:                if (ret <= 0)
                    317:                        goto fail;
1.3       martijn   318:                if ((pdu = snmp_unpackage(agent, buf, ret)) == NULL) {
1.2       martijn   319:                        tries--;
                    320:                        direction = POLLOUT;
                    321:                        errno = EPROTO;
1.1       martijn   322:                        continue;
1.2       martijn   323:                }
1.1       martijn   324:                /* Validate pdu format and check request id */
                    325:                if (ber_scanf_elements(pdu, "{iSSe", &rreqid, &varbind) != 0 ||
1.2       martijn   326:                    varbind->be_encoding != BER_TYPE_SEQUENCE) {
                    327:                        errno = EPROTO;
                    328:                        direction = POLLOUT;
                    329:                        tries--;
                    330:                        continue;
                    331:                }
1.4     ! martijn   332:                if (rreqid != reqid && rreqid != 0) {
1.2       martijn   333:                        errno = EPROTO;
                    334:                        direction = POLLOUT;
                    335:                        tries--;
1.1       martijn   336:                        continue;
1.2       martijn   337:                }
1.1       martijn   338:                for (varbind = varbind->be_sub; varbind != NULL;
                    339:                    varbind = varbind->be_next) {
1.2       martijn   340:                        if (ber_scanf_elements(varbind, "{oS}", &oid) != 0) {
                    341:                                errno = EPROTO;
                    342:                                direction = POLLOUT;
                    343:                                tries--;
1.4     ! martijn   344:                                continue;
1.2       martijn   345:                        }
1.1       martijn   346:                }
                    347:
1.3       martijn   348:                free(message);
                    349:                return pdu;
                    350:        }
                    351:
                    352: fail:
                    353:        free(message);
                    354:        return NULL;
                    355: }
                    356:
                    357: static char *
                    358: snmp_package(struct snmp_agent *agent, struct ber_element *pdu, size_t *len)
                    359: {
                    360:        struct ber ber;
1.4     ! martijn   361:        struct ber_element *message, *scopedpdu = NULL;
        !           362:        ssize_t securitysize, ret;
        !           363:        char *securityparams = NULL, *packet = NULL;
        !           364:        long long msgid;
1.3       martijn   365:
                    366:        bzero(&ber, sizeof(ber));
                    367:        ber_set_application(&ber, smi_application);
                    368:
                    369:        if ((message = ber_add_sequence(NULL)) == NULL) {
                    370:                ber_free_elements(pdu);
                    371:                goto fail;
                    372:        }
                    373:
                    374:        switch (agent->version) {
                    375:        case SNMP_V1:
                    376:        case SNMP_V2C:
                    377:                if (ber_printf_elements(message, "dse", agent->version,
                    378:                    agent->community, pdu) == NULL) {
                    379:                        ber_free_elements(pdu);
                    380:                        goto fail;
                    381:                }
                    382:                break;
                    383:        case SNMP_V3:
1.4     ! martijn   384:                msgid = arc4random_uniform(2147483647);
        !           385:                if ((scopedpdu = ber_add_sequence(NULL)) == NULL) {
        !           386:                        ber_free_elements(pdu);
        !           387:                        goto fail;
        !           388:                }
        !           389:                if (ber_printf_elements(scopedpdu, "xxe",
        !           390:                    agent->v3->engineid, agent->v3->engineidlen,
        !           391:                    agent->v3->ctxname, agent->v3->ctxnamelen, pdu) == NULL) {
        !           392:                        ber_free_elements(pdu);
        !           393:                        ber_free_elements(scopedpdu);
        !           394:                        goto fail;
        !           395:                }
        !           396:                pdu = NULL;
        !           397:                if ((securityparams = agent->v3->sec->genparams(agent,
        !           398:                    &securitysize)) == NULL) {
        !           399:                        ber_free_elements(scopedpdu);
        !           400:                        goto fail;
        !           401:                }
        !           402:                if (ber_printf_elements(message, "d{idxd}xe",
        !           403:                    agent->version, msgid, UDP_MAXPACKET, &(agent->v3->level),
        !           404:                    (size_t) 1, agent->v3->sec->model, securityparams,
        !           405:                    securitysize, scopedpdu) == NULL)
        !           406:                        goto fail;
1.3       martijn   407:                break;
                    408:        }
                    409:
                    410:        if (ber_write_elements(&ber, message) == -1)
                    411:                goto fail;
                    412:        ret = ber_copy_writebuf(&ber, (void **)&packet);
                    413:
                    414:        *len = (size_t) ret;
                    415:        ber_free(&ber);
                    416:
                    417: fail:
                    418:        ber_free_elements(message);
1.4     ! martijn   419:        free(securityparams);
1.3       martijn   420:        return packet;
                    421: }
                    422:
                    423: static struct ber_element *
                    424: snmp_unpackage(struct snmp_agent *agent, char *buf, size_t buflen)
                    425: {
                    426:        struct ber ber;
                    427:        enum snmp_version version;
                    428:        char *community;
                    429:        struct ber_element *pdu;
1.4     ! martijn   430:        long long msgid, model;
        !           431:        int msgsz;
        !           432:        char *msgflags, *secparams;
        !           433:        size_t msgflagslen, secparamslen;
        !           434:        struct ber_element *message = NULL, *payload, *scopedpdu, *ctxname;
        !           435:        off_t secparamsoffset;
        !           436:        char *engineid;
        !           437:        size_t engineidlen;
1.3       martijn   438:
                    439:        bzero(&ber, sizeof(ber));
                    440:        ber_set_application(&ber, smi_application);
                    441:
                    442:        ber_set_readbuf(&ber, buf, buflen);
                    443:        if ((message = ber_read_elements(&ber, NULL)) == NULL)
                    444:                return NULL;
                    445:        ber_free(&ber);
                    446:
                    447:        if (ber_scanf_elements(message, "{de", &version, &payload) != 0)
                    448:                goto fail;
                    449:
                    450:        if (version != agent->version)
                    451:                goto fail;
                    452:
1.4     ! martijn   453:        switch (version) {
1.3       martijn   454:        case SNMP_V1:
                    455:        case SNMP_V2C:
                    456:                if (ber_scanf_elements(payload, "se", &community, &pdu) == -1)
                    457:                        goto fail;
                    458:                if (strcmp(community, agent->community) != 0)
                    459:                        goto fail;
                    460:                ber_unlink_elements(payload);
1.1       martijn   461:                ber_free_elements(message);
                    462:                return pdu;
1.3       martijn   463:        case SNMP_V3:
1.4     ! martijn   464:                if (ber_scanf_elements(payload, "{idxi}pxe", &msgid, &msgsz,
        !           465:                    &msgflags, &msgflagslen, &model, &secparamsoffset,
        !           466:                    &secparams, &secparamslen, &scopedpdu) == -1)
        !           467:                        goto fail;
        !           468:                if (msgflagslen != 1)
        !           469:                        goto fail;
        !           470:                if (agent->v3->sec->parseparams(agent, buf, buflen,
        !           471:                    secparamsoffset, secparams, secparamslen,
        !           472:                    msgflags[0]) == -1)
        !           473:                        goto fail;
        !           474:                if (ber_scanf_elements(scopedpdu, "{xeS{", &engineid,
        !           475:                    &engineidlen, &ctxname) == -1)
        !           476:                        goto fail;
        !           477:                if (!agent->v3->engineidset) {
        !           478:                        if (snmp_v3_setengineid(agent->v3, engineid,
        !           479:                            engineidlen) == -1)
        !           480:                                goto fail;
        !           481:                }
        !           482:                pdu = ber_unlink_elements(ctxname);
        !           483:                /* Accept reports, so we can continue if possible */
        !           484:                if (pdu->be_type != SNMP_C_REPORT) {
        !           485:                        if ((msgflags[0] & SNMP_MSGFLAG_SECMASK) !=
        !           486:                            (agent->v3->level & SNMP_MSGFLAG_SECMASK))
        !           487:                                goto fail;
        !           488:                }
        !           489:
        !           490:                ber_free_elements(message);
        !           491:                return pdu;
1.1       martijn   492:        }
1.3       martijn   493:        /* NOTREACHED */
1.1       martijn   494:
                    495: fail:
                    496:        ber_free_elements(message);
                    497:        return NULL;
1.3       martijn   498: }
                    499:
                    500: ssize_t
                    501: ber_copy_writebuf(struct ber *ber, void **buf)
                    502: {
                    503:        char *bbuf;
                    504:        ssize_t ret;
                    505:
                    506:        *buf = NULL;
                    507:        if ((ret = ber_get_writebuf(ber, (void **)&bbuf)) == -1)
                    508:                return -1;
                    509:        if ((*buf = malloc(ret)) == NULL)
                    510:                return -1;
                    511:        memcpy(*buf, bbuf, ret);
                    512:        return  ret;
1.1       martijn   513: }