*
* Written by Russ Allbery <eagle@eyrie.org>
* Based on work by David Mazières
+ * Copyright 2016, 2020, 2023 Russ Allbery <eagle@eyrie.org>
* Copyright 2014
* The Board of Trustees of the Leland Stanford Junior University
*
- * See LICENSE for licensing terms.
+ * SPDX-License-Identifier: MIT
*/
#include <config.h>
#include <portable/system.h>
#ifdef HAVE_SQLITE3_H
-# include <sqlite3.h>
+# include <sqlite3.h>
#endif
#include <plugin/internal.h>
* prefix and the prefix with the last character incremented; the suffix query
* gets the same, but the suffix should be reversed.
*/
+/* clang-format off */
#define PREFIX_QUERY \
"SELECT password, drowssap FROM passwords WHERE password BETWEEN ? AND ?;"
#define SUFFIX_QUERY \
"SELECT password, drowssap FROM passwords WHERE drowssap BETWEEN ? AND ?;"
+/* clang-format on */
/*
/* Get CDB dictionary path from krb5.conf. */
strength_config_string(ctx, "password_dictionary_sqlite", &path);
- /* If it was set, report an error, since we don't have CDB support. */
+ /* If it was set, report an error, since we don't have SQLite support. */
if (path == NULL)
return 0;
free(path);
- krb5_set_error_message(ctx, KADM5_BAD_SERVER_PARAMS, "SQLite dictionary"
- " requested but not built with SQLite support");
+ krb5_set_error_message(ctx, KADM5_BAD_SERVER_PARAMS,
+ "SQLite dictionary requested but not built with"
+ " SQLite support");
return KADM5_BAD_SERVER_PARAMS;
}
#endif
#ifdef HAVE_SQLITE3
/*
- * Report a SQLite error. Takes the SQLite error code and the Kerberos
- * context, stores the resulting error in the Kerberos context, and returns
- * the generic KADM5_FAILURE code, since there doesn't appear to be anything
- * better.
+ * Report a SQLite error. Takes the module data (used to access the SQLite
+ * object) and the Kerberos context, stores the SQLite error in the Kerberos
+ * context, and returns the generic KADM5_FAILURE code, since there doesn't
+ * appear to be anything better.
*/
-static krb5_error_code
-error_sqlite(krb5_context ctx, int status, const char *format, ...)
+static krb5_error_code __attribute__((__format__(printf, 3, 4)))
+error_sqlite(krb5_context ctx, krb5_pwqual_moddata data, const char *format,
+ ...)
{
va_list args;
ssize_t length;
char *message;
- const char *errstr;
-
- errstr = sqlite3_errstr(status);
+ const char *errmsg;
+
+ errmsg = sqlite3_errmsg(data->sqlite);
va_start(args, format);
length = vasprintf(&message, format, args);
va_end(args);
if (length < 0)
return strength_error_system(ctx, "cannot allocate memory");
- krb5_set_error_message(ctx, KADM5_FAILURE, "%s: %s", message, errstr);
+ krb5_set_error_message(ctx, KADM5_FAILURE, "%s: %s", message, errmsg);
free(message);
return KADM5_FAILURE;
}
{
size_t i;
- for (i = 0; a[i] == b[i] && a[i] != '\0' && b[i] != '\0'; i++)
+ for (i = 0; a[i] == b[i] && a[i] != '\0'; i++)
;
return i;
}
*
* To see why the sum of the prefix and suffix length can be longer than the
* length of the password when the password doesn't match the word, consider
- * the password "aaaa" and the word "aaaaaaaaa"
- * (The prefix length plus the
+ * the password "aaaa" and the word "aaaaaaaaa". The prefix length plus the
* suffix length may be greater than the length of the password if the
- * password is an exact match for the word or
+ * password is an exact match for the word or an initial or final substring of
+ * the word.
*/
static bool
match(size_t length, const char *password, const char *drowssap,
/* Open the database. */
status = sqlite3_open_v2(path, &data->sqlite, SQLITE_OPEN_READONLY, NULL);
if (status != 0)
- return error_sqlite(ctx, status, "cannot open dictionary %s", path);
+ return error_sqlite(ctx, data, "cannot open dictionary %s", path);
/* Precompile the queries we'll use. */
status = sqlite3_prepare_v2(data->sqlite, PREFIX_QUERY, -1,
&data->prefix_query, NULL);
if (status != 0)
- return error_sqlite(ctx, status, "cannot prepare prefix query");
+ return error_sqlite(ctx, data, "cannot prepare prefix query");
status = sqlite3_prepare_v2(data->sqlite, SUFFIX_QUERY, -1,
&data->suffix_query, NULL);
if (status != 0)
- return error_sqlite(ctx, status, "cannot prepare suffix query");
+ return error_sqlite(ctx, data, "cannot prepare suffix query");
/* Finished. Return success. */
+ free(path);
return 0;
}
const char *password)
{
krb5_error_code code;
- size_t length, prefix_length, suffix_length;
+ size_t length;
+ int prefix_length, suffix_length;
char *prefix = NULL;
char *drowssap = NULL;
bool found = false;
* Determine the length of the prefix and suffix into which we'll divide
* the string. Passwords shorter than two characters cannot be
* meaningfully checked using this method and cause boundary condition
- * problems.
+ * problems. Passwords longer than INT_MAX cannot be passed to the SQLite
+ * library.
*/
length = strlen(password);
- if (length < 2)
+ if (length < 2 || length > INT_MAX)
return 0;
- prefix_length = length / 2;
- suffix_length = length - prefix_length;
+ prefix_length = (int) length / 2;
+ suffix_length = (int) length - prefix_length;
/* Obtain the reversed password, used for suffix checks. */
drowssap = reverse_string(password);
status = sqlite3_bind_text(data->prefix_query, 1, password, prefix_length,
NULL);
if (status != SQLITE_OK) {
- code = error_sqlite(ctx, status, "cannot bind prefix start");
+ code = error_sqlite(ctx, data, "cannot bind prefix start");
goto fail;
}
prefix[prefix_length - 1]++;
- status = sqlite3_bind_text(data->prefix_query, 2, prefix, prefix_length,
- NULL);
+ status =
+ sqlite3_bind_text(data->prefix_query, 2, prefix, prefix_length, NULL);
if (status != SQLITE_OK) {
- code = error_sqlite(ctx, status, "cannot bind prefix end");
+ code = error_sqlite(ctx, data, "cannot bind prefix end");
goto fail;
}
break;
}
if (status != SQLITE_DONE && status != SQLITE_ROW) {
- code = error_sqlite(ctx, status, "error searching by password prefix");
+ code = error_sqlite(ctx, data, "error searching by password prefix");
goto fail;
}
status = sqlite3_reset(data->prefix_query);
if (status != SQLITE_OK) {
- code = error_sqlite(ctx, status, "error resetting prefix query");
+ code = error_sqlite(ctx, data, "error resetting prefix query");
goto fail;
}
if (found)
status = sqlite3_bind_text(data->suffix_query, 1, drowssap, suffix_length,
SQLITE_TRANSIENT);
if (status != SQLITE_OK) {
- code = error_sqlite(ctx, status, "cannot bind suffix start");
+ code = error_sqlite(ctx, data, "cannot bind suffix start");
goto fail;
}
drowssap[prefix_length - 1]++;
SQLITE_TRANSIENT);
drowssap[prefix_length - 1]--;
if (status != SQLITE_OK) {
- code = error_sqlite(ctx, status, "cannot bind suffix end");
+ code = error_sqlite(ctx, data, "cannot bind suffix end");
goto fail;
}
break;
}
if (status != SQLITE_DONE && status != SQLITE_ROW) {
- code = error_sqlite(ctx, status, "error searching by password suffix");
+ code = error_sqlite(ctx, data, "error searching by password suffix");
goto fail;
}
status = sqlite3_reset(data->suffix_query);
if (status != SQLITE_OK) {
- code = error_sqlite(ctx, status, "error resetting suffix query");
+ code = error_sqlite(ctx, data, "error resetting suffix query");
goto fail;
}
if (found)
goto found;
/* No match. Clean up and return success. */
- memset(prefix, 0, length);
- memset(drowssap, 0, length);
+ explicit_bzero(prefix, length);
+ explicit_bzero(drowssap, length);
free(prefix);
free(drowssap);
return 0;
code = strength_error_dict(ctx, ERROR_DICT);
fail:
- memset(prefix, 0, length);
- memset(drowssap, 0, length);
+ if (prefix != NULL)
+ explicit_bzero(prefix, length);
+ explicit_bzero(drowssap, length);
free(prefix);
free(drowssap);
return code;
if (data->suffix_query != NULL)
sqlite3_finalize(data->suffix_query);
if (data->sqlite != NULL)
- sqlite3_close_v2(data->sqlite);
+ sqlite3_close(data->sqlite);
}
-#endif /* HAVE_CDB */
+#endif /* HAVE_SQLITE3 */