Hi, On 26/04/2021 19:44, Gert Doering wrote: > The existing code was doing far too much work for too little > gain - copying the string segment for scanf(), checking extra > for spaces, making the result quite unreadable. > > Verify each segment with (short-circuited) isxdigit() checks, > then feed directly to scanf(), which will stop parsing on ':' > or end-of-string. > > Rewrite error message to differenciate "hash too short" (including
differenciate -> differenTiate > number of bytes read) and "hash too long" (it did not terminate when > we had enough bytes). > > While at it, add an option printer for the resulting o->verify_hash > list to show_settings(). > Thanks for this patch! Simplifying existing code is as important as introducing new features and this proves that the project is still well maintained. (Feature-ACK) > Signed-off-by: Gert Doering <g...@greenie.muc.de> > --- > src/openvpn/options.c | 64 +++++++++++++++++++++---------------------- > 1 file changed, 32 insertions(+), 32 deletions(-) > > diff --git a/src/openvpn/options.c b/src/openvpn/options.c > index 2a5b1393..15472878 100644 > --- a/src/openvpn/options.c > +++ b/src/openvpn/options.c > @@ -1082,56 +1082,43 @@ string_substitute(const char *src, int from, int to, > struct gc_arena *gc) > static struct verify_hash_list * > parse_hash_fingerprint(const char *str, int nbytes, int msglevel, struct > gc_arena *gc) > { > - int i; > + int i=0; spaces around the '=' > const char *cp = str; > > struct verify_hash_list *ret; > ALLOC_OBJ_CLEAR_GC(ret, struct verify_hash_list, gc); > > - char term = 1; > + char term = 0; > int byte; > - char bs[3]; > > - for (i = 0; i < nbytes; ++i) > + while (*cp && i < nbytes) > { > - if (strlen(cp) < 2) > + /* valid segments consist of exactly two hex digits, then ':' or EOS > */ > + if (!isxdigit(cp[0]) > + || !isxdigit(cp[1]) since we have a limit of 80 chars per line, how about putting the two conditions above on the same line? > + || (cp[2] != ':' && cp[2] != '\0') > + || sscanf(cp, "%x", &byte) != 1) same for this two, but less important as they are not really related. Regarding the format string, we concluded on IRC that %hhx is part of C99 - why not using it here then? > { > msg(msglevel, "format error in hash fingerprint: %s", str); > + break; We had no break here because this function is always invoked with the FATAL bit set in the msglevel, therefore the msg() call will terminate the program. This said, I prefer moving away from this assumption, so I personally welcome the break. > } > - bs[0] = *cp++; > - bs[1] = *cp++; > - bs[2] = 0; > > - /* the format string "%x" passed to sscanf will ignore any space and > - * will still try to parse the other character. However, this is not > - * expected format for a fingerprint, therefore explictly check for > - * blanks in the string and error out if any is found > - */ > - if (bs[0] == ' ' || bs[1] == ' ') > - { > - msg(msglevel, "format error in hash fingerprint unexpected > blank: %s", > - str); > - } > + ret->hash[i++] = (uint8_t)byte; This cast would go away if byte is declared as uint8_t and we use %hhx in the sscanf(). > > - byte = 0; > - if (sscanf(bs, "%x", &byte) != 1) > - { > - msg(msglevel, "format error in hash fingerprint hex byte: %s", > str); > - } > - ret->hash[i] = (uint8_t)byte; > - term = *cp++; > - if (term != ':' && term != 0) > - { > - msg(msglevel, "format error in hash fingerprint delimiter: %s", > str); > - } > - if (term == 0) > + term = cp[2]; > + if (term == '\0') do we really need "term" at al at this point? we could directly use cp[2] in the if condition. > { > break; > } > + cp += 3; > } > - if (term != 0 || i != nbytes-1) > + if (i < nbytes) > { > - msg(msglevel, "hash fingerprint is different length than expected > (%d bytes): %s", nbytes, str); > + msg(msglevel, "hash fingerprint is wrong length - expected %d bytes, > got %d: %s", nbytes, i, str); > + } > + else if (term != '\0') same here: if we hit the else branch, it means that cp[0,1,2] are all defined so we can again compare cp[2] to '\0'. > + { > + msg(msglevel, "hash fingerprint too long - expected only %d bytes: > %s", nbytes, str); > } > return ret; > } > @@ -1791,6 +1778,19 @@ show_settings(const struct options *o) > } > } > SHOW_STR(remote_cert_eku); > + if (o->verify_hash) > + { > + struct gc_arena gc = gc_new(); > + struct verify_hash_list *hl = o->verify_hash; > + while (hl) > + { > + char *s = format_hex_ex(hl->hash, sizeof(hl->hash), 0, > + 1, ":", &gc); > + SHOW_PARM(verify_hash, s, "%s"); > + hl = hl->next; > + } > + gc_free(&gc); > + } Should we also print options->verify_hash_depth while at it? > SHOW_INT(ssl_flags); > > SHOW_INT(tls_timeout); > Rest looks good! I performed a bunch of tests with various strings and they all passed as expected. Regards, -- Antonio Quartulli _______________________________________________ Openvpn-devel mailing list Openvpn-devel@lists.sourceforge.net https://lists.sourceforge.net/lists/listinfo/openvpn-devel