bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2002-2018 Dovecot authors, see the included COPYING file */
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainenstatic const char *
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainenfix_format_real(const char *fmt, const char *p, size_t *len_r)
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen /* we'll assume that there's only one %m in the format string.
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen this simplifies the code and there's really no good reason to have
1dcf22e98ba8310e8daa8c9297936c6f3a645e7aPhil Carmody it multiple times. Callers can trap this case themselves. */
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen /* @UNSAFE */
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen if (**p == '*') {
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen /* We don't bother supporting "*m$" - it's not used
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen anywhere and seems a bit dangerous. */
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen /* Limit to 4 digits - we'll never want more than that.
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen Some implementations might not handle long digits
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen correctly, or maybe even could be used for DoS due
9bf221d642eba214b81346abd9ba2f9b9f2eb937Phil Carmody to using too much CPU. If you want to express '99'
9bf221d642eba214b81346abd9ba2f9b9f2eb937Phil Carmody as '00099', then you lose in this function. */
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen unsigned int i = 0;
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainenstatic const char *
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainenprintf_format_fix_noalloc(const char *format, size_t *len_r)
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* NOTE: This function is overly strict in what it accepts. Some
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen format strings that are valid (and safe) in C99 will cause a panic
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen here. This is because we don't really need to support the weirdest
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen special cases, and we're also being extra careful not to pass
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen anything to the underlying libc printf, which might treat the string
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen differently than us and unexpectedly handling it as %n. For example
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen "%**%n" with glibc. */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* Allow only the standard C99 flags. There are also <'> and <I> flags,
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen but we don't really need them. And at worst if they're not supported
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen by the underlying printf, they could potentially be used to work
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen around our restrictions. */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* As a tiny optimization keep the most commonly used conversion
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen specifiers first, so strchr() stops early. */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen static const char *printf_specifiers = "sudcixXpoeEfFgGaA";
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (*p == '%') {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* we'll be strict and allow %% only when there are no
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen optinal flags or modifiers. */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* 1) zero or more flags. We'll add a further restriction that
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen each flag can be used only once, since there's no need to
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen use them more than once, and some implementations might
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen add their own limits. */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen bool printf_flags_seen[N_ELEMENTS(printf_flags)] = { FALSE, };
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen while (*p != '\0' &&
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen i_panic("Duplicate %% flag '%c' starting at #%u in '%s'",
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* 2) Optional minimum field width */
686a3be710a464396cbfbc7b05eaa7fe16f3cd1cTimo Sirainen i_panic("Too large minimum field width starting at #%u in '%s'",
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* 3) Optional precision */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (*p == '.') {
85174bd658f999cb3aa145885fd85674f8595fbbTimo Sirainen i_panic("Too large precision starting at #%u in '%s'",
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* 4) Optional length modifier */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen switch (*p) {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (*++p == 'h')
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (*++p == 'l')
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen /* 5) conversion specifier */
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen if (*p == '\0' || strchr(printf_specifiers, *p) == NULL) {
ebe00087d3c7f9706d4acb9017eaad912404516cTimo Sirainen switch (*p) {
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen i_panic("Missing %% specifier starting at #%u in '%s'",
c398eca6b0fc6583687bd6fe2ee2dbcca2ae9387Timo Sirainen i_panic("Unsupported 0x%02x specifier starting at #%u in '%s'",
2ac5f36aa7c2e7a07ba8815d43a6d7483f62e74cTimo Sirainenconst char *printf_format_fix_get_len(const char *format, size_t *len_r)
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen ret = printf_format_fix_noalloc(format, len_r);
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainenconst char *printf_format_fix(const char *format)
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainen ret = printf_format_fix_noalloc(format, &len);
5dd05e966ffd69181ab3067f6939b03ced68ebc3Timo Sirainenconst char *printf_format_fix_unsafe(const char *format)