]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
Merge psp-deb1.ntp.org:/home/stenn/ntp-stable
authorHarlan Stenn <stenn@ntp.org>
Mon, 27 Nov 2017 09:58:16 +0000 (09:58 +0000)
committerHarlan Stenn <stenn@ntp.org>
Mon, 27 Nov 2017 09:58:16 +0000 (09:58 +0000)
into  psp-deb1.ntp.org:/net/nfs1/nfs/home/stenn/ntp-stable-aes128cmac

bk: 5a1be1b8kiy67hpyZMFoekD1Vbm-sQ

1  2 
ChangeLog
libntp/a_md5encrypt.c
libntp/ssl_init.c
ntpq/ntpq-subs.c
ntpq/ntpq.c
sntp/crypto.c
sntp/tests/crypto.c
sntp/tests/packetProcessing.c

diff --cc ChangeLog
index add12209e575fbbf7bddf2f6236e3a7444551cea,ae2fd19545d068f8b963d9028420286c02a5dcf4..7b0301db46cf94bbfcf42b85c36e044dae8433be
+++ b/ChangeLog
@@@ -1,53 -1,7 +1,54 @@@
  ---
  
 -* AES-128-CMAC support
 +* [Bug 3441] Validate the assumption that AF_UNSPEC is 0.  stenn@ntp.org
 +* [Bug 3439] When running multiple commands / hosts in ntpq... <perlinger@ntp.org>
 + - applied patch by ggarvey
 +* [Bug 3438] Negative values and values > 999 days in... <perlinger@ntp.org>
 + - applied patch by ggarvey (with minor mods)
 +* [Bug 3437] ntpd tries to open socket with AF_UNSPEC domain
 + - applied patch (with mods) by Miroslav Lichvar <perlinger@ntp.org>
 +* [Bug 3435] anchor NTP era alignment <perlinger@ntp.org>
 +* [Bug 3430] ntpq dumps core (SIGSEGV) for "keytype md2"
 +  - fixed several issues with hash algos in ntpd, sntp, ntpq,
 +    ntpdc and the test suites <perlinger@ntp.org>
 +* [Bug 3424] Trimble Thunderbolt 1024 week millenium bug <perlinger@ntp.org>
 +  - initial patch by Daniel Pouzzner
 +* [Bug 3423] QNX adjtime() implementation error checking is
 +  wrong <perlinger@ntp.org>
 +* [Bug 3417] ntpq ifstats packet counters can be negative
 +  made IFSTATS counter quantities unsigned <perlinger@ntp.org>
 +* [Bug 3411] problem about SIGN(6) packet handling for ntp-4.2.8p10
 +  - raised receive buffer size to 1200 <perlinger@ntp.org>
 +* [Bug 3408] refclock_jjy.c: Avoid a wrong report of the coverity static
 +  analysis tool. <abe@ntp.org>
 +* [Bug 3399] NTP: linker error in 4.2.8p10 during Linux cross-compilation
 +  - initial patch by timeflies@mail2tor.com  <perlinger@ntp.org>
 +* [Bug 3398] tests fail with core dump <perlinger@ntp.org>
 +  - patch contributed by Alexander Bluhm
 +* [Bug 3397] ctl_putstr() asserts that data fits in its buffer
 +  rework of formatting & data transfer stuff in 'ntp_control.c'
 +  avoids unecessary buffers and size limitations. <perlinger@ntp.org>
 +* [Bug 3394] Leap second deletion does not work on ntpd clients
 +  - fixed handling of dynamic deletion w/o leap file <perlinger@ntp.org>
 +* [Bug 3391] ntpd segfaults on startup due to small warmup thread stack size
 +  - increased mimimum stack size to 32kB <perlinger@ntp.org>
 +* [Bug 3367] Faulty LinuxPPS NMEA clock support in 4.2.8 <perlinger@ntp.org>
 +  - reverted handling of PPS kernel consumer to 4.2.6 behavior
 +* [Bug 3365] Updates driver40(-ja).html and miscopt.html <abe@ntp.org>
  * [Bug 3358] Spurious KoD log messages in .INIT. phase.  HStenn.
 +* [Bug 3016] wrong error position reported for bad ":config pool"
 +  - fixed location counter & ntpq output <perlinger@ntp.org>
 +* [Bug 2737] Wrong phone number listed for USNO. ntp-bugs@bodosom.net,
 +  perlinger@ntp.org
 +* [Bug 2557] Fix Thunderbolt init. ntp-bugs@bodosom.net, perlinger@ntp.
 +* [Bug 948] Trustedkey config directive leaks memory. <perlinger@ntp.org>
 +* Use strlcpy() to copy strings, not memcpy().  HStenn.
 +* Typos.  HStenn.
 +* test_ntp_scanner_LDADD needs ntpd/ntp_io.o.  HStenn.
 +* refclock_jjy.c: Add missing "%s" to an msyslog() call.  HStenn.
 +* Build ntpq and libntpq.a with NTP_HARD_*FLAGS.  perlinger@ntp.org
 +* Fix bug in the override portion of the compiler hardening macro. HStenn.
++* AES-128-CMAC support
  
  ---
  (4.2.8p10) 2017/03/21 Released by Harlan Stenn <stenn@ntp.org>
index eef9515f97cbd0ce133c7a7a501a34c611d760c6,b7bb83145b5a819652778b45b824e4bd36d50d3f..7ec7e57f2c9f2d2d9b62385bda1dbb30f566b820
@@@ -44,12 -125,16 +125,16 @@@ MD5authencrypt
        EVP_DigestUpdate(ctx, key, cache_secretsize);
        EVP_DigestUpdate(ctx, (u_char *)pkt, length);
        EVP_DigestFinal(ctx, digest, &len);
+ #endif
        EVP_MD_CTX_free(ctx);
+ #ifdef OPENSSL
+       }
+ #endif
        /* If the MAC is longer than the MAX then truncate it. */
 -      if (len > MAX_MAC_LEN - 4)
 -          len = MAX_MAC_LEN - 4;
 -      memmove((u_char *)pkt + length + 4, digest, len);
 -      return (len + 4);
 +      if (len > MAX_MDG_LEN)
 +          len = MAX_MDG_LEN;
 +      memmove((u_char *)pkt + length + KEY_MAC_LEN, digest, len);
 +      return (len + KEY_MAC_LEN);
  }
  
  
@@@ -77,21 -162,100 +162,100 @@@ MD5authdecrypt
         * was created.
         */
        INIT_SSL();
-       ctx = EVP_MD_CTX_new();
-       if (!(ctx && EVP_DigestInit(ctx, EVP_get_digestbynid(type)))) {
-               msyslog(LOG_ERR,
-                   "MAC decrypt: digest init failed");
-               EVP_MD_CTX_free(ctx);
-               return (0);
+ #ifdef OPENSSL
+       /* Check if CMAC key type specific code required */
+       if (cache_type == NID_cmac) {
+           CMAC_CTX *      ctx;
+           if (debug) {
+               fprintf(stderr, "%s:%d:%s():%s:nid\n",
+                               __FILE__, __LINE__, __func__, CMAC);
+           }
+           if (!(ctx = CMAC_CTX_new())) {
+               fprintf(stderr,  "MAC decrypt: CMAC %s CTX new failed.\n", CMAC);
+               msyslog(LOG_ERR, "MAC decrypt: CMAC %s CTX new failed.",   CMAC);
+               len = 0;
+           } else
+           if (!CMAC_Init(ctx, key, (u_int)AES_128_KEY_SIZE,
+                                               EVP_aes_128_cbc(), NULL)) {
+               fprintf(stderr,  "MAC decrypt: CMAC %s Init failed.\n",    CMAC);
+               msyslog(LOG_ERR, "MAC decrypt: CMAC %s Init failed.",      CMAC);
+               len = 0;
+           } else
+           if (!CMAC_Update(ctx, (u_char *)pkt, (u_int)length)) {
+               fprintf(stderr,  "MAC decrypt: CMAC %s Update failed.\n",  CMAC);
+               msyslog(LOG_ERR, "MAC decrypt: CMAC %s Update failed.",    CMAC);
+               len = 0;
+           } else
+           if (!CMAC_Final(ctx, digest, &len)) {
+               fprintf(stderr,  "MAC decrypt: CMAC %s Final failed.\n",   CMAC);
+               msyslog(LOG_ERR, "MAC decrypt: CMAC %s Final failed.",     CMAC);
+               len = 0;
+           }
+           CMAC_CTX_cleanup(ctx);
+       } else {        /* generic MAC handling */
+ #endif
+           EVP_MD_CTX *        ctx;
+           if (!(ctx = EVP_MD_CTX_new())) {
+               fprintf(stderr,  "MAC decrypt: MAC %s Digest CTX new failed.\n",
+                                                       OBJ_nid2sn(type));
+               msyslog(LOG_ERR, "MAC decrypt: MAC %s Digest CTX new failed.",
+                                                       OBJ_nid2sn(type));
+               len = 0;
+           }
+ #ifdef OPENSSL        /* OpenSSL 1 supports return codes 0 fail, 1 okay */
+           else
+           if (!EVP_DigestInit(ctx, EVP_get_digestbynid(type))) {
+               fprintf(stderr,  "MAC decrypt: MAC %s Digest Init failed.\n",
+                                                       OBJ_nid2sn(type));
+               msyslog(LOG_ERR, "MAC decrypt: MAC %s Digest Init failed.",
+                                                       OBJ_nid2sn(type));
+               len = 0;
+           } else
+           if (!EVP_DigestUpdate(ctx, key, (u_int)cache_secretsize)) {
+               fprintf(stderr,  "MAC decrypt: MAC %s Digest Update key failed.\n",
+                                                       OBJ_nid2sn(type));
+               msyslog(LOG_ERR, "MAC decrypt: MAC %s Digest Update key failed.",
+                                                       OBJ_nid2sn(type));
+               len = 0;
+           } else
+           if (!EVP_DigestUpdate(ctx, (u_char *)pkt, (u_int)length)) {
+               fprintf(stderr,  "MAC decrypt: MAC %s Digest Update data failed.\n",
+                                                       OBJ_nid2sn(type));
+               msyslog(LOG_ERR, "MAC decrypt: MAC %s Digest Update data failed.",
+                                                       OBJ_nid2sn(type));
+               len = 0;
+           } else
+           if (!EVP_DigestFinal(ctx, digest, &len)) {
+               fprintf(stderr,  "MAC decrypt: MAC %s Digest Final failed.\n",
+                                                       OBJ_nid2sn(type));
+               msyslog(LOG_ERR, "MAC decrypt: MAC %s Digest Final failed.",
+                                                       OBJ_nid2sn(type));
+               len = 0;
+           }
+ #else /* !OPENSSL */
+           if (!(ctx && EVP_DigestInit(ctx, EVP_get_digestbynid(type)))) {
+                   msyslog(LOG_ERR,
+                       "MAC decrypt: digest init failed");
+                   EVP_MD_CTX_free(ctx);
+                   return (0);
+           }
+           EVP_DigestUpdate(ctx, key, cache_secretsize);
+           EVP_DigestUpdate(ctx, (u_char *)pkt, (u_int)length);
+           EVP_DigestFinal(ctx, digest, &len);
+ #endif
+           EVP_MD_CTX_free(ctx);
+ #ifdef OPENSSL
        }
-       EVP_DigestUpdate(ctx, key, cache_secretsize);
-       EVP_DigestUpdate(ctx, (u_char *)pkt, length);
-       EVP_DigestFinal(ctx, digest, &len);
-       EVP_MD_CTX_free(ctx);
+ #endif
        /* If the MAC is longer than the MAX then truncate it. */
 -      if (len > MAX_MAC_LEN - 4)
 -          len = MAX_MAC_LEN - 4;
 -      if (size != (size_t)len + 4) {
 +      if (len > MAX_MDG_LEN)
 +          len = MAX_MDG_LEN;
 +      if (size != (size_t)len + KEY_MAC_LEN) {
                msyslog(LOG_ERR,
                    "MAC decrypt: MAC length error");
                return (0);
index a3c163b34e3772c9e71a4a39a3baa9ac2d2ca2fa,71f2eda04b539070882721b4644b879ce318c89f..2dc3ea841b65a28fa2e3f431f4bcdfafa4a25b49
@@@ -94,55 -100,90 +100,93 @@@ keytype_from_text
  {
        int             key_type;
        u_int           digest_len;
-       /*----------------------------------------------------------- */
 -#ifdef OPENSSL
 -      const u_long    max_digest_len = MAX_MAC_LEN - sizeof(keyid_t);
 +#ifdef OPENSSL        /* --*-- OpenSSL code --*-- */
-       /*----------------------------------------------------------- */
        char *          upcased;
        char *          pch;
 +      EVP_MD const *  md;
  
        /*
         * OpenSSL digest short names are capitalized, so uppercase the
         * digest name before passing to OBJ_sn2nid().  If it is not
-        * recognized but begins with 'M' or 'm' use NID_md5 to be
-        * consistent with past behavior.
+        * recognized but matches our CMAC string use NID_cmac, or if
 -       * it begins with 'M' use NID_md5 to be consistent with past
 -       * behavior.
++       * it begins with 'M' or 'm' use NID_md5 to be consistent with
++       * past behavior.
         */
        INIT_SSL();
 +
 +      /* get name in uppercase */
        LIB_GETBUF(upcased);
        strlcpy(upcased, text, LIB_BUFLENGTH);
-       for (pch = upcased; '\0' != *pch; ++pch)
+       for (pch = upcased; '\0' != *pch; pch++) {
                *pch = (char)toupper((unsigned char)*pch);
+       }
  
-       md = EVP_get_digestbyname(upcased);
-       if (NULL == md && !strcmp(upcased, "M"))
-               md = EVP_get_digestbyname("MD5");
-       if (NULL == md)
-               return 0;
+       key_type = OBJ_sn2nid(upcased);
  
-       key_type   = EVP_MD_type(md);
-       digest_len = EVP_MD_size(md);
+       if (!key_type && !strncmp(CMAC, upcased, strlen(CMAC) + 1)) {
+               key_type = NID_cmac;
  
-       /*----------------------------------------------------------- */
- #else /* --*-- NON-SSL CODE --*-- */
-       /*----------------------------------------------------------- */
+               if (debug) {
+                       fprintf(stderr, "%s:%d:%s():%s:key\n",
+                               __FILE__, __LINE__, __func__, CMAC);
+               }
+       }
+ #else
 +
-       if ('m' == tolower((unsigned char)text[0]))
-               key_type = NID_md5;
-       else
-               return 0;
+       key_type = 0;
+ #endif
  
-       digest_len = 16;
+       if (!key_type && 'm' == tolower((unsigned char)text[0])) {
+               key_type = NID_md5;
+       }
  
-       /*----------------------------------------------------------- */
- #endif        /* --*-- NON-SSL CODE --*-- */
-       /*----------------------------------------------------------- */
+       if (!key_type) {
+               return 0;
+       }
  
-       if (pdigest_len)
+       if (NULL != pdigest_len) {
+ #ifdef OPENSSL
+               const EVP_MD *  md = EVP_get_digestbynid(key_type);
+               digest_len = (md) ? EVP_MD_size(md) : 0;
+               if (!md || digest_len <= 0) {
+                   if (key_type == NID_cmac) {
+                       digest_len = CMAC_LENGTH;
+                       if (debug) {
+                               fprintf(stderr, "%s:%d:%s():%s:len\n",
+                                       __FILE__, __LINE__, __func__, CMAC);
+                       }
+                   } else {
+                       fprintf(stderr,
+                               "key type %s is not supported by OpenSSL\n",
+                               keytype_name(key_type));
+                       msyslog(LOG_ERR,
+                               "key type %s is not supported by OpenSSL\n",
+                               keytype_name(key_type));
+                       return 0;
+                   }
+               }
+               if (digest_len > max_digest_len) {
+                   fprintf(stderr,
+                           "key type %s %u octet digests are too big, max %lu\n",
+                           keytype_name(key_type), digest_len,
+                           max_digest_len);
+                   msyslog(LOG_ERR,
+                           "key type %s %u octet digests are too big, max %lu",
+                           keytype_name(key_type), digest_len,
+                           max_digest_len);
+                   return 0;
+               }
+ #else
+               digest_len = MD5_LENGTH;
+ #endif
                *pdigest_len = digest_len;
+       }
        return key_type;
  }
  
Simple merge
diff --cc ntpq/ntpq.c
index c8a9f8e8e38c04da8657bca2d7a3863ac639584b,3c3764e50389cf340d15c52bf5727542396e904d..fa35b69eccc224a1d7251b31f63bb39ed7f151d0
@@@ -3590,73 -3597,191 +3603,197 @@@ ntpq_custom_opt_handler
   * Obtain list of digest names
   */
  
 +#if defined(OPENSSL) && !defined(HAVE_EVP_MD_DO_ALL_SORTED)
 +# if defined(_MSC_VER) && OPENSSL_VERSION_NUMBER >= 0x10100000L
 +#  define HAVE_EVP_MD_DO_ALL_SORTED
 +# endif
 +#endif
 +
  #ifdef OPENSSL
  # ifdef HAVE_EVP_MD_DO_ALL_SORTED
+ #  define K_PER_LINE  8
+ #  define K_NL_PFX_STR        "\n    "
+ #  define K_DELIM_STR ", "
  struct hstate {
     char *list;
     const char **seen;
     int idx;
  };
- #define K_PER_LINE 8
- #define K_NL_PFX_STR "\n    "
- #define K_DELIM_STR ", "
static void list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg )
+ static void
list_md_fn(const EVP_MD *m, const char *from, const char *to, void *arg)
  {
 -    size_t len, n;
 -    const char *name, *cp, **seen;
 +    size_t      len, n, digest_len;
 +    const char          *name, **seen;
      struct hstate *hstate = arg;
  
-     if (!m)
-           return; /* Ignore aliases */
+     /* m is MD obj, from is name or alias, to is base name for alias */
+     if (!m || !from || to) {
+         return; /* Ignore aliases */
+     }
+     /* Discard MACs that NTP won't accept. */
+     /* Keep this consistent with keytype_from_text() in ssl_init.c. */
+     if (EVP_MD_size(m) > (MAX_MAC_LEN - sizeof(keyid_t))) {
+         return;
+     }
  
-     /* There are duplicates.  Discard if name has been seen.
-      *
-      * Names are capitalized in 'keytype_from_text()' in ssl_init.c; we
-      * have to make sure we do compare case-insensitive when checking
-      * for dupes...
-      */
      name = EVP_MD_name(m);
-     len  = strlen(name) + 1;
-     for (seen = hstate->seen; *seen; seen++)
-           if (!strcasecmp(*seen, name))
-                   return;
-     
-     /* Discard MACs that NTP won't accept.
-      * Keep this consistent with keytype_from_text() in ssl_init.c,
-      * which is done most easily by using it...
-      */
-     if (keytype_from_text(name, &digest_len) == 0)
+     /* Lowercase names aren't accepted by keytype_from_text in ssl_init.c */
+     for (cp = name; *cp; cp++) {
+       if (islower((unsigned char)*cp)) {
+           return;
+       }
+     }
+     len = (cp - name) + 1;
+     /* There are duplicates.  Discard if name has been seen. */
+     for (seen = hstate->seen; *seen; seen++) {
+         if (!strcmp(*seen, name)) {
            return;
+       }
+     }
  
      n = (seen - hstate->seen) + 2;
      hstate->seen = erealloc(hstate->seen, n * sizeof(*seen));
      hstate->seen[n-2] = name;
      hstate->seen[n-1] = NULL;
  
-     if (hstate->list != NULL)
-           len += strlen(hstate->list);
-     len += (hstate->idx >= K_PER_LINE)? strlen(K_NL_PFX_STR): strlen(K_DELIM_STR);
+     if (hstate->list != NULL) {
+       len += strlen(hstate->list);
+     }
+     len += (hstate->idx >= K_PER_LINE)
+               ? strlen(K_NL_PFX_STR)
+               : strlen(K_DELIM_STR);
  
      if (hstate->list == NULL) {
-           hstate->list = (char *)emalloc(len);
-           hstate->list[0] = '\0';
 -      hstate->list = (char *)emalloc(len);
++        hstate->list = (char *)emalloc(len);
+       hstate->list[0] = '\0';
      } else {
-           hstate->list = (char *)erealloc(hstate->list, len);
+       hstate->list = (char *)erealloc(hstate->list, len);
      }
-     
      sprintf(hstate->list + strlen(hstate->list), "%s%s",
-           ((hstate->idx >= K_PER_LINE)? K_NL_PFX_STR : K_DELIM_STR),
+           ((hstate->idx >= K_PER_LINE) ? K_NL_PFX_STR : K_DELIM_STR),
            name);
-     if (hstate->idx >= K_PER_LINE)
-           hstate->idx = 1;
-     else
-           hstate->idx++;
+     if (hstate->idx >= K_PER_LINE) {
+       hstate->idx = 1;
+     } else {
+       hstate->idx++;
+     }
+ }
+ /* Insert CMAC into SSL digests list */
+ static char *
+ insert_cmac(char *list)
+ {
+     int insert;
+     size_t len;
+     /* If list empty, we need to insert CMAC on new line */
+     insert = (!list || !*list);
+     if (insert) {
+       len = strlen(K_NL_PFX_STR) + strlen(CMAC);
+       list = (char *)erealloc(list, len + 1);
+       sprintf(list, "%s%s", K_NL_PFX_STR, CMAC);
+     } else {  /* List not empty */
+       /* Check if CMAC already in list - future proofing */
+       const char *cmac_sn;
+       char *cmac_p;
+       cmac_sn = OBJ_nid2sn(NID_cmac);
+       cmac_p = list;
+       insert = cmac_sn != NULL && *cmac_sn != '\0';
+       /* CMAC in list if found, followed by nul char or ',' */
+       while (insert && NULL != (cmac_p = strstr(cmac_p, cmac_sn))) {
+           cmac_p += strlen(cmac_sn);
+           /* Still need to insert if not nul and not ',' */
+           insert = *cmac_p && ',' != *cmac_p;
+       }
+       /* Find proper insertion point */
+       if (insert) {
+           char *last_nl;
+           char *point;
+           char *delim;
+           int found;
+           /* Default to start if list empty */
+           found = 0;
+           delim = list;
+           len = strlen(list);
+           /* While new lines */
+           while (delim < list + len && *delim &&
+                       !strncmp(K_NL_PFX_STR, delim, strlen(K_NL_PFX_STR))) {
+               point = delim + strlen(K_NL_PFX_STR);
+               /* While digest names on line */
+               while (point < list + len && *point) {
+                   /* Another digest after on same or next line? */
+                   delim = strstr( point, K_DELIM_STR);
+                   last_nl = strstr( point, K_NL_PFX_STR);
+                   /* No - end of list */
+                   if (!delim && !last_nl) {
+                       delim = list + len;
+                   } else
+                   /* New line and no delim or before delim? */
+                   if (last_nl && (!delim || last_nl < delim)) {
+                       delim = last_nl;
+                   }
+                   /* Found insertion point where CMAC before entry? */
+                   if (strncmp(CMAC, point, delim - point) < 0) {
+                       found = 1;
+                       break;
+                   }
+                   if (delim < list + len && *delim &&
+                           !strncmp(K_DELIM_STR, delim, strlen(K_DELIM_STR))) {
+                       point += strlen(K_DELIM_STR);
+                   } else {
+                       break;
+                   }
+               } /* While digest names on line */
+           } /* While new lines */
+           /* If found in list */
+           if (found) {
+               /* insert cmac and delim */
+               /* Space for list could move - save offset */
+               ptrdiff_t p_offset = point - list;
+               len += strlen(CMAC) + strlen(K_DELIM_STR);
+               list = (char *)erealloc(list, len + 1);
+               point = list + p_offset;
+               /* move to handle src/dest overlap */
+               memmove(point + strlen(CMAC) + strlen(K_DELIM_STR),
+                                       point, strlen(point) + 1);
+               strncpy(point, CMAC, strlen(CMAC));
+               strncpy(point + strlen(CMAC), K_DELIM_STR, strlen(K_DELIM_STR));
+           } else {    /* End of list */
+               /* append delim and cmac */
+               len += strlen(K_DELIM_STR) + strlen(CMAC);
+               list = (char *)erealloc(list, len + 1);
+               strcpy(list + strlen(list), K_DELIM_STR);
+               strcpy(list + strlen(list), CMAC);
+           }
+       } /* insert */
+     } /* List not empty */
+     return list;
  }
  # endif
  #endif
diff --cc sntp/crypto.c
index 1158b3cac2463e336cac1abed371c00b27e426f6,a0275ff47e608852066bd2de3315e67ad304ff34..04e34e9e8a04b0b4e661266c8f868da7ff19db62
@@@ -139,10 -220,11 +222,12 @@@ auth_init
                if (octothorpe)
                        *octothorpe = '\0';
                act = emalloc(sizeof(*act));
-               scan_cnt = sscanf(kbuf, "%d %19s %128s", &act->key_id, act->typen, keystring);
+               /* keep width 15 = sizeof struct key.type - 1 synced */
+               scan_cnt = sscanf(kbuf, "%d %15s %128s",
+                                       &act->key_id, act->type, keystring);
                if (scan_cnt == 3) {
                        int len = strlen(keystring);
 +                      goodline = 1;   /* assume best for now */
                        if (len <= 20) {
                                act->key_len = len;
                                memcpy(act->key_seq, keystring, len + 1);
Simple merge
Simple merge