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

Diff for /src/usr.bin/snmp/usm.c between version 1.1 and 1.2

version 1.1, 2019/09/18 09:48:14 version 1.2, 2019/09/18 09:52:47
Line 41 
Line 41 
         int engineidset;          int engineidset;
         char *engineid;          char *engineid;
         size_t engineidlen;          size_t engineidlen;
           enum usm_key_level authlevel;
           const EVP_MD *digest;
           char *authkey;
         int bootsset;          int bootsset;
         uint32_t boots;          uint32_t boots;
         int timeset;          int timeset;
Line 48 
Line 51 
         struct timespec timecheck;          struct timespec timecheck;
 };  };
   
   struct usm_cookie {
           size_t digestoffset;
   };
   
 static int usm_doinit(struct snmp_agent *);  static int usm_doinit(struct snmp_agent *);
 static char *usm_genparams(struct snmp_agent *, size_t *);  static char *usm_genparams(struct snmp_agent *, size_t *, void **);
   static int usm_finalparams(struct snmp_agent *, char *, size_t, size_t, void *);
 static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,  static int usm_parseparams(struct snmp_agent *, char *, size_t, off_t, char *,
     size_t, uint8_t);      size_t, uint8_t);
   static void usm_digest_pos(void *, size_t);
 static void usm_free(void *);  static void usm_free(void *);
   static char *usm_passwd2mkey(const EVP_MD *, const char *);
   static char *usm_mkey2lkey(struct usm_sec *, const EVP_MD *, const char *);
   static size_t usm_digestlen(const EVP_MD *);
   
 struct snmp_sec *  struct snmp_sec *
 usm_init(const char *user, size_t userlen)  usm_init(const char *user, size_t userlen)
Line 84 
Line 96 
         sec->init = usm_doinit;          sec->init = usm_doinit;
         sec->genparams = usm_genparams;          sec->genparams = usm_genparams;
         sec->parseparams = usm_parseparams;          sec->parseparams = usm_parseparams;
           sec->finalparams = usm_finalparams;
         sec->free = usm_free;          sec->free = usm_free;
           sec->freecookie = free;
         sec->data = usm;          sec->data = usm;
         return sec;          return sec;
 }  }
Line 119 
Line 133 
 }  }
   
 static char *  static char *
 usm_genparams(struct snmp_agent *agent, size_t *len)  usm_genparams(struct snmp_agent *agent, size_t *len, void **cookie)
 {  {
         struct ber ber;          struct ber ber;
         struct ber_element *params;          struct ber_element *params, *digestelm;
         struct usm_sec *usm = agent->v3->sec->data;          struct usm_sec *usm = agent->v3->sec->data;
           char digest[USM_MAX_DIGESTLEN];
           size_t digestlen = 0;
         char *secparams = NULL;          char *secparams = NULL;
         ssize_t berlen = 0;          ssize_t berlen = 0;
           struct usm_cookie *usmcookie;
         struct timespec now, timediff;          struct timespec now, timediff;
         uint32_t boots, time;          uint32_t boots, time;
   
           bzero(digest, sizeof(digest));
   
           if ((usmcookie = calloc(1, sizeof(*usmcookie))) == NULL)
                   return NULL;
           *cookie = usmcookie;
   
         if (usm->timeset) {          if (usm->timeset) {
                 if (clock_gettime(CLOCK_MONOTONIC, &now) == -1)                  if (clock_gettime(CLOCK_MONOTONIC, &now) == -1) {
                           free(usmcookie);
                         return NULL;                          return NULL;
                   }
                 timespecsub(&now, &(usm->timecheck), &timediff);                  timespecsub(&now, &(usm->timecheck), &timediff);
                 time = usm->time + timediff.tv_sec;                  time = usm->time + timediff.tv_sec;
         } else          } else
                 time = 0;                  time = 0;
         boots = usm->boots;          boots = usm->boots;
           if (agent->v3->level & SNMP_MSGFLAG_AUTH)
                   digestlen = usm_digestlen(usm->digest);
         if ((params = ber_printf_elements(NULL, "{xddxxx}", usm->engineid,          if ((params = ber_printf_elements(NULL, "{xddxxx}", usm->engineid,
             usm->engineidlen, boots, time, usm->user, usm->userlen, NULL,              usm->engineidlen, boots, time, usm->user, usm->userlen, digest,
             (size_t) 0, NULL, (size_t) 0)) == NULL)              digestlen, NULL, (size_t) 0)) == NULL) {
                   free(usmcookie);
                 return NULL;                  return NULL;
           }
   
           if (ber_scanf_elements(params, "{SSSSe",  &digestelm) == -1) {
                   ber_free_element(params);
                   free(usmcookie);
                   return NULL;
           }
   
           ber_set_writecallback(digestelm, usm_digest_pos, usmcookie);
   
         bzero(&ber, sizeof(ber));          bzero(&ber, sizeof(ber));
         ber_set_application(&ber, smi_application);          ber_set_application(&ber, smi_application);
         if (ber_write_elements(&ber, params) != -1)          if (ber_write_elements(&ber, params) != -1)
Line 155 
Line 191 
 }  }
   
 static int  static int
   usm_finalparams(struct snmp_agent *agent, char *buf, size_t buflen,
       size_t secparamsoffset, void *cookie)
   {
           struct usm_sec *usm = agent->v3->sec->data;
           struct usm_cookie *usmcookie = cookie;
           u_char digest[EVP_MAX_MD_SIZE];
   
           if ((agent->v3->level & SNMP_MSGFLAG_AUTH) == 0)
                   return 0;
   
           if (usm->authlevel != USM_KEY_LOCALIZED)
                   return -1;
   
           if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), buf,
               buflen, digest, NULL) == NULL)
                   return -1;
   
           memcpy(buf + secparamsoffset + usmcookie->digestoffset, digest,
               usm_digestlen(usm->digest));
           return 0;
   }
   
   static int
 usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,  usm_parseparams(struct snmp_agent *agent, char *packet, size_t packetlen,
     off_t secparamsoffset, char *buf, size_t buflen, uint8_t level)      off_t secparamsoffset, char *buf, size_t buflen, uint8_t level)
 {  {
         struct usm_sec *usm = agent->v3->sec->data;          struct usm_sec *usm = agent->v3->sec->data;
         struct ber ber;          struct ber ber;
         struct ber_element *secparams;          struct ber_element *secparams;
         char *engineid, *user;          char *engineid, *user, *digest;
         size_t engineidlen, userlen;          size_t engineidlen, userlen, digestlen;
         struct timespec now, timediff;          struct timespec now, timediff;
           off_t digestoffset;
           char exp_digest[EVP_MAX_MD_SIZE];
         uint32_t boots, time;          uint32_t boots, time;
   
         bzero(&ber, sizeof(ber));          bzero(&ber, sizeof(ber));
           bzero(exp_digest, sizeof(exp_digest));
   
         ber_set_application(&ber, smi_application);          ber_set_application(&ber, smi_application);
         ber_set_readbuf(&ber, buf, buflen);          ber_set_readbuf(&ber, buf, buflen);
Line 174 
Line 236 
                 return -1;                  return -1;
         ber_free(&ber);          ber_free(&ber);
   
         if (ber_scanf_elements(secparams, "{xddxSS}", &engineid, &engineidlen,          if (ber_scanf_elements(secparams, "{xddxpxS}", &engineid, &engineidlen,
             &boots, &time, &user, &userlen) == -1)              &boots, &time, &user, &userlen, &digestoffset, &digest,
               &digestlen) == -1)
                 goto fail;                  goto fail;
   
         if (!usm->engineidset) {          if (!usm->engineidset) {
Line 225 
Line 288 
             memcmp(user, usm->user, userlen) != 0)              memcmp(user, usm->user, userlen) != 0)
                 goto fail;                  goto fail;
   
           if (level & SNMP_MSGFLAG_AUTH) {
                   if (digestlen != usm_digestlen(usm->digest))
                           goto fail;
           }
           if ((agent->v3->level & SNMP_MSGFLAG_AUTH)) {
                   bzero(packet + secparamsoffset + digestoffset, digestlen);
                   if (HMAC(usm->digest, usm->authkey, EVP_MD_size(usm->digest), packet,
                       packetlen, exp_digest, NULL) == NULL)
                           goto fail;
   
                   if (memcmp(exp_digest, digest, digestlen) != 0)
                           goto fail;
           } else
                   if (digestlen != 0)
                           goto fail;
   
         ber_free_element(secparams);          ber_free_element(secparams);
         return 0;          return 0;
   
Line 234 
Line 313 
 }  }
   
 static void  static void
   usm_digest_pos(void *data, size_t offset)
   {
           struct usm_cookie *usmcookie = data;
   
           usmcookie->digestoffset = offset;
   }
   
   static void
 usm_free(void *data)  usm_free(void *data)
 {  {
         struct usm_sec *usm = data;          struct usm_sec *usm = data;
   
         free(usm->user);          free(usm->user);
           free(usm->authkey);
         free(usm->engineid);          free(usm->engineid);
         free(usm);          free(usm);
 }  }
   
 int  int
   usm_setauth(struct snmp_sec *sec, const EVP_MD *digest, const char *key,
       size_t keylen, enum usm_key_level level)
   {
           struct usm_sec *usm = sec->data;
           char *lkey;
   
           /*
            * We could transform a master key to a local key here if we already
            * have usm_setengineid called. Sine snmpc.c is the only caller at
            * the moment there's no need, since it always calls this function
            * first.
            */
           if (level == USM_KEY_PASSWORD) {
                   if ((usm->authkey = usm_passwd2mkey(digest, key)) == NULL)
                           return -1;
                   level = USM_KEY_MASTER;
                   keylen = EVP_MD_size(digest);
           } else {
                   if (keylen != (size_t)EVP_MD_size(digest)) {
                           errno = EINVAL;
                           return -1;
                   }
                   if ((lkey = malloc(keylen)) == NULL)
                           return -1;
                   memcpy(lkey, key, keylen);
                   usm->authkey = lkey;
           }
           usm->digest = digest;
           usm->authlevel = level;
           return 0;
   }
   
   int
 usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)  usm_setengineid(struct snmp_sec *sec, char *engineid, size_t engineidlen)
 {  {
         struct usm_sec *usm = sec->data;          struct usm_sec *usm = sec->data;
           char *mkey;
   
         if (usm->engineid != NULL)          if (usm->engineid != NULL)
                 free(usm->engineid);                  free(usm->engineid);
Line 256 
Line 378 
         usm->engineidlen = engineidlen;          usm->engineidlen = engineidlen;
         usm->engineidset = 1;          usm->engineidset = 1;
   
           if (usm->authlevel == USM_KEY_MASTER) {
                   mkey = usm->authkey;
                   if ((usm->authkey = usm_mkey2lkey(usm, usm->digest,
                       mkey)) == NULL) {
                           usm->authkey = mkey;
                           return -1;
                   }
                   free(mkey);
                   usm->authlevel = USM_KEY_LOCALIZED;
           }
   
         return 0;          return 0;
 }  }
   
Line 272 
Line 405 
         usm->time = time;          usm->time = time;
         usm->timeset = 1;          usm->timeset = 1;
         return 0;          return 0;
   }
   
   static char *
   usm_passwd2mkey(const EVP_MD *md, const char *passwd)
   {
           EVP_MD_CTX ctx;
           int i, count;
           const u_char *pw;
           u_char *c;
           u_char keybuf[EVP_MAX_MD_SIZE];
           unsigned dlen;
           char *key;
   
           bzero(&ctx, sizeof(ctx));
           EVP_DigestInit_ex(&ctx, md, NULL);
           pw = (const u_char *)passwd;
           for (count = 0; count < 1048576; count += 64) {
                   c = keybuf;
                   for (i = 0; i < 64; i++) {
                           if (*pw == '\0')
                                   pw = (const u_char *)passwd;
                           *c++ = *pw++;
                   }
                   EVP_DigestUpdate(&ctx, keybuf, 64);
           }
           EVP_DigestFinal_ex(&ctx, keybuf, &dlen);
           EVP_MD_CTX_cleanup(&ctx);
   
           if ((key = malloc(dlen)) == NULL)
                   return NULL;
           memcpy(key, keybuf, dlen);
           return key;
   }
   
   static char *
   usm_mkey2lkey(struct usm_sec *usm, const EVP_MD *md, const char *mkey)
   {
           EVP_MD_CTX ctx;
           u_char buf[EVP_MAX_MD_SIZE];
           u_char *lkey;
           unsigned lklen;
   
           bzero(&ctx, sizeof(ctx));
           EVP_DigestInit_ex(&ctx, md, NULL);
   
           EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md));
           EVP_DigestUpdate(&ctx, usm->engineid, usm->engineidlen);
           EVP_DigestUpdate(&ctx, mkey, EVP_MD_size(md));
   
           EVP_DigestFinal_ex(&ctx, buf, &lklen);
           EVP_MD_CTX_cleanup(&ctx);
   
           if ((lkey = malloc(lklen)) == NULL)
                   return NULL;
           memcpy(lkey, buf, lklen);
           return lkey;
   }
   
   static size_t
   usm_digestlen(const EVP_MD *md)
   {
           switch (EVP_MD_type(md)) {
           case NID_md5:
           case NID_sha1:
                   return 12;
           case NID_sha224:
                   return 16;
           case NID_sha256:
                   return 24;
           case NID_sha384:
                   return 32;
           case NID_sha512:
                   return 48;
           default:
                   return 0;
           }
 }  }

Legend:
Removed from v.1.1  
changed lines
  Added in v.1.2