From: Juergen Perlinger Date: Mon, 19 Oct 2020 06:15:03 +0000 (+0200) Subject: [Bug 3689] Extension for MD5, SHA-1 and other keys X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=bd50818dfa444836a9d981fe49b0b6d283cfe07c;p=thirdparty%2Fntp.git [Bug 3689] Extension for MD5, SHA-1 and other keys - refactor decoding a passwd string / secret - have ntp{q,dc} use the same password decoding ntpd uses bk: 5f8d2ee74IiW5oemnU69vB9LwoPlzA --- diff --git a/ChangeLog b/ChangeLog index eeceaa9f1..7bb391137 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,9 @@ +--- +* [Bug 3689] Extension for MD5, SHA-1 and other keys + - ntp{q,dc} now use the same password processing as ntpd does in the key + file, so havin a binary secret >= 11 bytes is possible for all keys. + (This is a different approach to the roblem than suggested) + --- (4.2.8p15) 2020/06/23 Released by Harlan Stenn diff --git a/include/ntp_stdlib.h b/include/ntp_stdlib.h index 265aafa73..873e9d90f 100644 --- a/include/ntp_stdlib.h +++ b/include/ntp_stdlib.h @@ -67,8 +67,13 @@ extern int xsbprintf(char**, char* const, char const*, ...) NTP_PRINTF(3, 4); typedef void (*ctrl_c_fn)(void); /* authkeys.c */ +#define AUTHPWD_UNSPEC 0 +#define AUTHPWD_PLAIN 1 +#define AUTHPWD_HEX 2 + extern void auth_delkeys (void); extern int auth_havekey (keyid_t); +extern size_t authdecodepw (u_char *dst, size_t dstlen, const char *src, int fmt); extern int authdecrypt (keyid_t, u_int32 *, size_t, size_t); extern size_t authencrypt (keyid_t, u_int32 *, size_t); extern int authhavekey (keyid_t); diff --git a/libntp/authkeys.c b/libntp/authkeys.c index 7c1cbb065..bf9628b73 100644 --- a/libntp/authkeys.c +++ b/libntp/authkeys.c @@ -927,3 +927,95 @@ authdecrypt( cache_secret, cache_secretsize, pkt, length, size); } + +/* password decoding helpers */ +static size_t +pwdecode_plain( + u_char * dst, + size_t dstlen, + const char * src + ) +{ + size_t srclen = strlen(src); + if (srclen > dstlen) { + errno = ENOMEM; + return (size_t)-1; + } + memcpy(dst, src, srclen); + return srclen; +} + +static size_t +pwdecode_hex( + u_char * dst, + size_t dstlen, + const char * src + ) +{ + static const char hex[] = "00112233445566778899AaBbCcDdEeFf"; + + size_t srclen = strlen(src); + size_t reslen = (srclen >> 1) + (srclen & 1); + u_char tmp; + char *ptr; + size_t j; + + if (reslen > dstlen) { + errno = ENOMEM; + reslen = (size_t)-1; + } else { + for (j = 0; j < srclen; ++j) { + tmp = *(const unsigned char*)(src + j); + ptr = strchr(hex, tmp); + if (ptr == NULL) { + errno = EINVAL; + reslen = (size_t)-1; + break; + } + tmp = (u_char)((ptr - hex) > 1); + if (j & 1) + dst[j >> 1] |= tmp; + else + dst[j >> 1] = tmp << 4; + } + } + return reslen; +} +/* + * authdecodepw - decode plaintext or hex-encoded password to binary + * secret. Returns size of secret in bytes or -1 on error. + */ +size_t +authdecodepw( + u_char * dst, + size_t dstlen, + const char * src, + int fmt + ) +{ + size_t reslen; + + if ( !(dst && dstlen && src)) { + errno = EINVAL; + reslen = (size_t)-1; + } else { + switch (fmt) { + case AUTHPWD_UNSPEC: + if (strlen(src) <= 20) + reslen = pwdecode_plain(dst, dstlen, src); + else + reslen = pwdecode_hex(dst, dstlen, src); + break; + case AUTHPWD_PLAIN: + reslen = pwdecode_plain(dst, dstlen, src); + break; + case AUTHPWD_HEX: + reslen = pwdecode_hex(dst, dstlen, src); + break; + default: + errno = EINVAL; + reslen = (size_t)-1; + } + } + return reslen; +} diff --git a/libntp/authreadkeys.c b/libntp/authreadkeys.c index 48c5c4d5e..adf53beae 100644 --- a/libntp/authreadkeys.c +++ b/libntp/authreadkeys.c @@ -38,7 +38,7 @@ nexttok( */ while (*cp == ' ' || *cp == '\t') cp++; - + /* * Save this and space to end of token */ @@ -46,19 +46,19 @@ nexttok( while (*cp != '\0' && *cp != '\n' && *cp != ' ' && *cp != '\t' && *cp != '#') cp++; - + /* * If token length is zero return an error, else set end of * token to zero and return start. */ if (starttok == cp) return NULL; - + if (*cp == ' ' || *cp == '\t') *cp++ = '\0'; else *cp = '\0'; - + *str = cp; return starttok; } @@ -114,7 +114,7 @@ free_keydata( ) { KeyAccT *kap; - + if (node) { while (node->keyacclist) { kap = node->keyacclist; @@ -142,9 +142,8 @@ authreadkeys( keyid_t keyno; int keytype; char buf[512]; /* lots of room for line */ - u_char keystr[32]; /* Bug 2537 */ + u_char keystr[64]; /* Bug 2537 */ size_t len; - size_t j; u_int nerr; KeyDataT *list = NULL; KeyDataT *next = NULL; @@ -172,7 +171,7 @@ authreadkeys( token = nexttok(&line); if (token == NULL) continue; - + /* * First is key number. See if it is okay. */ @@ -208,10 +207,10 @@ authreadkeys( * have to process the line completely and have to * finally throw away the result... This is a bit more * work, but it also results in better error detection. - */ + */ #ifdef OPENSSL /* - * The key type is the NID used by the message digest + * The key type is the NID used by the message digest * algorithm. There are a number of inconsistencies in * the OpenSSL database. We attempt to discover them * here and prevent use of inconsistent data later. @@ -258,45 +257,33 @@ authreadkeys( continue; } next = NULL; - len = strlen(token); - if (len <= 20) { /* Bug 2537 */ - next = emalloc(sizeof(KeyDataT) + len); - next->keyacclist = NULL; - next->keyid = keyno; - next->keytype = keytype; - next->seclen = len; - memcpy(next->secbuf, token, len); - } else { - static const char hex[] = "0123456789abcdef"; - u_char temp; - char *ptr; - size_t jlim; - - jlim = min(len, 2 * sizeof(keystr)); - for (j = 0; j < jlim; j++) { - ptr = strchr(hex, tolower((unsigned char)token[j])); - if (ptr == NULL) - break; /* abort decoding */ - temp = (u_char)(ptr - hex); - if (j & 1) - keystr[j / 2] |= temp; - else - keystr[j / 2] = temp << 4; - } - if (j < jlim) { + len = authdecodepw(keystr, sizeof(keystr), token, AUTHPWD_UNSPEC); + if (len > sizeof(keystr)) { + switch (errno) { + case ENOMEM: + log_maybe(&nerr, + "authreadkeys: passwd too long for key %d", + keyno); + break; + case EINVAL: log_maybe(&nerr, - "authreadkeys: invalid hex digit for key %d", + "authreadkeys: passwd has bad char for key %d", keyno); - continue; + break; + default: + log_maybe(&nerr, + "authreadkeys: unknown errno %d for key %d", + errno, keyno); + break; } - len = jlim/2; /* hmmmm.... what about odd length?!? */ - next = emalloc(sizeof(KeyDataT) + len); - next->keyacclist = NULL; - next->keyid = keyno; - next->keytype = keytype; - next->seclen = len; - memcpy(next->secbuf, keystr, len); + continue; } + next = emalloc(sizeof(KeyDataT) + len); + next->keyacclist = NULL; + next->keyid = keyno; + next->keytype = keytype; + next->seclen = len; + memcpy(next->secbuf, keystr, len); token = nexttok(&line); if (token != NULL) { /* A comma-separated IP access list */ @@ -369,7 +356,7 @@ authreadkeys( next = NULL; continue; } - + INSIST(NULL != next); next->next = list; list = next; diff --git a/libntp/authusekey.c b/libntp/authusekey.c index ff449d3df..abc762188 100644 --- a/libntp/authusekey.c +++ b/libntp/authusekey.c @@ -10,11 +10,8 @@ #include "ntp_stdlib.h" /* - * Types of ascii representations for keys. "Standard" means a 64 bit - * hex number in NBS format, i.e. with the low order bit of each byte - * a parity bit. "NTP" means a 64 bit key in NTP format, with the - * high order bit of each byte a parity bit. "Ascii" means a 1-to-8 - * character string whose ascii representation is used as the key. + * Only used by ntp{q,dc} to set the key/algo/secret triple to use. + * Uses the same decoding scheme ntpd uses for keys in the key file. */ int authusekey( @@ -24,11 +21,14 @@ authusekey( ) { size_t len; - - len = strlen((const char *)str); - if (0 == len) + u_char buf[64]; + + len = authdecodepw(buf, sizeof(buf), (const char*)str, + AUTHPWD_UNSPEC); + if (len < 1 || len > sizeof(buf)) return 0; - - MD5auth_setkey(keyno, keytype, str, len, NULL); + + MD5auth_setkey(keyno, keytype, buf, len, NULL); + memset(buf, 0, sizeof(buf)); return 1; }