summaryrefslogtreecommitdiffstats
path: root/src/lib-otp/otp-parse.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 17:36:47 +0000
commit0441d265f2bb9da249c7abf333f0f771fadb4ab5 (patch)
tree3f3789daa2f6db22da6e55e92bee0062a7d613fe /src/lib-otp/otp-parse.c
parentInitial commit. (diff)
downloaddovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.tar.xz
dovecot-0441d265f2bb9da249c7abf333f0f771fadb4ab5.zip
Adding upstream version 1:2.3.21+dfsg1.upstream/1%2.3.21+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/lib-otp/otp-parse.c')
-rw-r--r--src/lib-otp/otp-parse.c253
1 files changed, 253 insertions, 0 deletions
diff --git a/src/lib-otp/otp-parse.c b/src/lib-otp/otp-parse.c
new file mode 100644
index 0000000..70b6940
--- /dev/null
+++ b/src/lib-otp/otp-parse.c
@@ -0,0 +1,253 @@
+/*
+ * OTP extended response parser.
+ *
+ * Copyright (c) 2006 Andrey Panin <pazke@donpac.ru>
+ *
+ * This software is released under the MIT license.
+ */
+
+#include "lib.h"
+#include "buffer.h"
+#include "str.h"
+#include "strfuncs.h"
+#include "hex-binary.h"
+
+#include "otp.h"
+
+#include <ctype.h>
+
+#define IS_LWS(c) ((c) == ' ' || (c) == '\t')
+
+static inline const char *otp_skip_lws(const char *data)
+{
+ while (*data != '\0' && IS_LWS(*data))
+ data++;
+ return data;
+}
+
+static inline bool otp_check_tail(const char *data)
+{
+ data = otp_skip_lws(data);
+
+ return *data != 0;
+}
+
+int otp_read_hex(const char *data, const char **endptr, unsigned char *hash)
+{
+ string_t *str;
+ buffer_t buf;
+ unsigned int i = 0;
+
+ if (data == NULL)
+ return -1;
+
+ str = t_str_new(18);
+ buffer_create_from_data(&buf, hash, OTP_HASH_SIZE);
+
+ while (*data != '\0') {
+ char c = *data;
+
+ if (i_isxdigit(c)) {
+ str_append_c(str, c);
+ if (++i == OTP_HASH_SIZE * 2) {
+ data++;
+ break;
+ }
+ } else if (!IS_LWS(c)) {
+ *endptr = data;
+ return -1;
+ }
+ data++;
+ }
+
+ *endptr = data;
+
+ if (i < OTP_HASH_SIZE * 2)
+ return -1;
+
+ return hex_to_binary(str_c(str), &buf);
+}
+
+#define add_word() do { \
+ tmp = otp_lookup_word(str_c(word)); \
+ buffer_append(&buf, &tmp, sizeof(tmp)); \
+ count++; \
+} while (0)
+
+int ATTR_NO_SANITIZE_INTEGER
+otp_read_words(const char *data, const char **endptr, unsigned char *hash)
+{
+ bool space = FALSE;
+ unsigned int len = 0, count = 0;
+ unsigned int parity = 0, bits[OTP_WORDS_NUMBER], tmp;
+ string_t *word;
+ buffer_t buf;
+
+ if (data == NULL)
+ return -1;
+
+ word = t_str_new(8);
+
+ data = otp_skip_lws(data);
+
+ buffer_create_from_data(&buf, bits, sizeof(bits));
+
+ for (; *data != '\0' && (count < OTP_WORDS_NUMBER); data++) {
+ char c = *data;
+
+ if (space) {
+ if (IS_LWS(c))
+ continue;
+ else if (i_isalpha(c)) {
+ str_append_c(word, c);
+ space = FALSE;
+ len = 1;
+ continue;
+ }
+ } else {
+ if (i_isalpha(c)) {
+ if (++len > OTP_MAX_WORD_LEN) {
+ count = 0;
+ break;
+ }
+ str_append_c(word, c);
+ continue;
+ } else if (IS_LWS(c)) {
+ add_word();
+ str_truncate(word, 0);
+ space = TRUE;
+ continue;
+ }
+ }
+ break;
+ }
+
+ if ((str_len(word) > 0) && (count == OTP_WORDS_NUMBER - 1))
+ add_word();
+
+ if (endptr != NULL)
+ *endptr = data;
+
+ if (count < OTP_WORDS_NUMBER)
+ return -1;
+
+ hash[0] = bits[0] >> 3;
+ hash[1] = ((bits[0] & 7) << 5) | (bits[1] >> 6);
+ hash[2] = ((bits[1] & 0x3f) << 2) | (bits[2] >> 9);
+ hash[3] = (bits[2] >> 1) & 0xff;
+ hash[4] = ((bits[2] & 3) << 7) | (bits[3] >> 4);
+ hash[5] = ((bits[3] & 15) << 4) | (bits[4] >> 7);
+ hash[6] = ((bits[4] & 0x7f) << 1) | (bits[5] >> 10);
+ hash[7] = (bits[5] >> 2) & 0xff;
+ parity = bits[5] & 3;
+
+ return otp_parity(hash) != parity ? 1 : 0;
+}
+
+int otp_read_new_params(const char *data, const char **endptr,
+ struct otp_state *state)
+{
+ const char *p, *s;
+ unsigned int i = 0;
+ int algo;
+
+ s = p = data;
+
+ while ((*p != 0) && !IS_LWS(*p)) p++;
+ if (*p == 0)
+ return -1;
+
+ algo = digest_find(t_strdup_until(s, p++));
+ if (algo < 0)
+ return -2;
+ state->algo = algo;
+
+ s = p;
+ if (str_parse_int(s, &state->seq, &p) < 0 || !IS_LWS(*p))
+ return -3;
+ p++;
+
+ while (i_isalnum(*p) && (i < OTP_MAX_SEED_LEN))
+ state->seed[i++] = i_tolower(*p++);
+ state->seed[i] = 0;
+
+ *endptr = p;
+ return 0;
+}
+
+int otp_parse_response(const char *data, unsigned char *hash, bool hex)
+{
+ const char *end;
+ int ret = hex ? otp_read_hex(data, &end, hash) :
+ otp_read_words(data, &end, hash);
+ if (ret < 0)
+ return ret;
+
+ return otp_check_tail(end) ? 1 : 0;
+}
+
+int otp_parse_init_response(const char *data, struct otp_state *new_state,
+ unsigned char *hash, bool hex, const char **error)
+{
+ const char *end;
+ int ret = hex ? otp_read_hex(data, &end, hash) :
+ otp_read_words(data, &end, hash);
+ if (ret < 0) {
+ *error = "invalid current OTP";
+ return ret;
+ }
+
+ end = otp_skip_lws(end);
+ if (*end++ != ':') {
+ *error = "missing colon";
+ return -1;
+ }
+
+ ret = otp_read_new_params(end, &end, new_state);
+ if (ret < 0) {
+ *error = "invalid OTP parameters";
+ return -1;
+ }
+
+ end = otp_skip_lws(end);
+ if (*end++ != ':') {
+ *error = "missing colon";
+ return -1;
+ }
+
+ ret = hex ? otp_read_hex(end, &end, new_state->hash) :
+ otp_read_words(end, &end, new_state->hash);
+ if (ret < 0) {
+ *error = "invalid new OTP";
+ return -1;
+ }
+
+ if (otp_check_tail(end)) {
+ *error = "trailing garbage found";
+ return -1;
+ }
+
+ return 0;
+}
+
+int otp_parse_dbentry(const char *text, struct otp_state *state)
+{
+ const char *end;
+ int ret;
+
+ ret = otp_read_new_params(text, &end, state);
+ if (ret != 0)
+ return ret;
+
+ if (*end++ != ' ')
+ return -1;
+
+ return otp_read_hex(end, &end, state->hash);
+}
+
+const char *otp_print_dbentry(const struct otp_state *state)
+{
+ return t_strdup_printf("%s %d %s %s", digest_name(state->algo),
+ state->seq, state->seed,
+ binary_to_hex(state->hash, 8));
+}