From: Timo Sirainen Date: Mon, 31 Oct 2016 19:48:16 +0000 (+0200) Subject: lib: API change - var_expand*() now returns error string. X-Git-Tag: 2.3.0.rc1~2702 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=0f5dc4da3982053036be65190e44bf28a67b1ca2;p=thirdparty%2Fdovecot%2Fcore.git lib: API change - var_expand*() now returns error string. This allows callers to fail properly if the format string is invalid. --- diff --git a/src/auth/auth-cache.c b/src/auth/auth-cache.c index 58f3dd5d2b..eaad8b1105 100644 --- a/src/auth/auth-cache.c +++ b/src/auth/auth-cache.c @@ -340,13 +340,21 @@ static const char * auth_request_expand_cache_key(const struct auth_request *request, const char *key) { + static bool error_logged = FALSE; + const char *value, *error; + /* Uniquely identify the request's passdb/userdb with the P/U prefix and by "%!", which expands to the passdb/userdb ID number. */ key = t_strconcat(request->userdb_lookup ? "U" : "P", "%!", request->master_user == NULL ? "" : "+%{master_user}", "\t", key, NULL); - return t_auth_request_var_expand(key, request, auth_cache_escape); + if (t_auth_request_var_expand(key, request, auth_cache_escape, + &value, &error) <= 0 && !error_logged) { + error_logged = TRUE; + i_error("Failed to expand auth cache key %s: %s", key, error); + } + return value; } const char * diff --git a/src/auth/auth-policy.c b/src/auth/auth-policy.c index cefd3b34f4..c79aba9395 100755 --- a/src/auth/auth-policy.c +++ b/src/auth/auth-policy.c @@ -465,9 +465,13 @@ void auth_policy_create_json(struct policy_lookup_ctx *context, const char *hashed_password = binary_to_hex(str_data(buffer), str_len(buffer)); str_append_c(context->json, '{'); var_table = policy_get_var_expand_table(context->request, hashed_password); - auth_request_var_expand_with_table(context->json, auth_policy_json_template, - context->request, var_table, - auth_policy_escape_function); + const char *error; + if (auth_request_var_expand_with_table(context->json, auth_policy_json_template, + context->request, var_table, + auth_policy_escape_function, &error) <= 0) { + auth_request_log_error(context->request, "policy", + "Failed to expand auth policy template: %s", error); + } if (include_success) { str_append(context->json, ",\"success\":"); if (!context->request->failed && context->request->successful && diff --git a/src/auth/auth-request-var-expand.c b/src/auth/auth-request-var-expand.c index f5d6c2d96d..ccc49f1f17 100644 --- a/src/auth/auth-request-var-expand.c +++ b/src/auth/auth-request-var-expand.c @@ -226,35 +226,39 @@ const struct var_expand_func_table auth_request_var_funcs_table[] = { { NULL, NULL } }; -void auth_request_var_expand(string_t *dest, const char *str, - const struct auth_request *auth_request, - auth_request_escape_func_t *escape_func) +int auth_request_var_expand(string_t *dest, const char *str, + const struct auth_request *auth_request, + auth_request_escape_func_t *escape_func, + const char **error_r) { - auth_request_var_expand_with_table(dest, str, auth_request, + return auth_request_var_expand_with_table(dest, str, auth_request, auth_request_get_var_expand_table(auth_request, escape_func), - escape_func); + escape_func, error_r); } -void auth_request_var_expand_with_table(string_t *dest, const char *str, - const struct auth_request *auth_request, - const struct var_expand_table *table, - auth_request_escape_func_t *escape_func) +int auth_request_var_expand_with_table(string_t *dest, const char *str, + const struct auth_request *auth_request, + const struct var_expand_table *table, + auth_request_escape_func_t *escape_func, + const char **error_r) { struct auth_request_var_expand_ctx ctx; memset(&ctx, 0, sizeof(ctx)); ctx.auth_request = auth_request; ctx.escape_func = escape_func == NULL ? escape_none : escape_func; - var_expand_with_funcs(dest, str, table, - auth_request_var_funcs_table, &ctx); + return var_expand_with_funcs(dest, str, table, + auth_request_var_funcs_table, &ctx, error_r); } -const char * -t_auth_request_var_expand(const char *str, - const struct auth_request *auth_request, - auth_request_escape_func_t *escape_func) +int t_auth_request_var_expand(const char *str, + const struct auth_request *auth_request ATTR_UNUSED, + auth_request_escape_func_t *escape_func ATTR_UNUSED, + const char **value_r, const char **error_r) { string_t *dest = t_str_new(128); - auth_request_var_expand(dest, str, auth_request, escape_func); - return str_c(dest); + int ret = auth_request_var_expand(dest, str, auth_request, + escape_func, error_r); + *value_r = str_c(dest); + return ret; } diff --git a/src/auth/auth-request-var-expand.h b/src/auth/auth-request-var-expand.h index 0d228daad2..7ad2b14a31 100644 --- a/src/auth/auth-request-var-expand.h +++ b/src/auth/auth-request-var-expand.h @@ -21,17 +21,19 @@ auth_request_get_var_expand_table_full(const struct auth_request *auth_request, auth_request_escape_func_t *escape_func, unsigned int *count) ATTR_NULL(2); -void auth_request_var_expand(string_t *dest, const char *str, - const struct auth_request *auth_request, - auth_request_escape_func_t *escape_func); -void auth_request_var_expand_with_table(string_t *dest, const char *str, - const struct auth_request *auth_request, - const struct var_expand_table *table, - auth_request_escape_func_t *escape_func); -const char * -t_auth_request_var_expand(const char *str, - const struct auth_request *auth_request, - auth_request_escape_func_t *escape_func); +int auth_request_var_expand(string_t *dest, const char *str, + const struct auth_request *auth_request, + auth_request_escape_func_t *escape_func, + const char **error_r); +int auth_request_var_expand_with_table(string_t *dest, const char *str, + const struct auth_request *auth_request, + const struct var_expand_table *table, + auth_request_escape_func_t *escape_func, + const char **error_r); +int t_auth_request_var_expand(const char *str, + const struct auth_request *auth_request, + auth_request_escape_func_t *escape_func, + const char **value_r, const char **error_r); const char *auth_request_str_escape(const char *string, const struct auth_request *request); diff --git a/src/auth/auth-request.c b/src/auth/auth-request.c index a715807e9a..f0de6233f5 100644 --- a/src/auth/auth-request.c +++ b/src/auth/auth-request.c @@ -798,7 +798,14 @@ static void auth_request_verify_plain_callback_finish(enum passdb_result result, struct auth_request *request) { - passdb_template_export(request->passdb->override_fields_tmpl, request); + const char *error; + + if (passdb_template_export(request->passdb->override_fields_tmpl, + request, &error) < 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand override_fields: %s", error); + result = PASSDB_RESULT_INTERNAL_FAILURE; + } if (!auth_request_handle_passdb_callback(&result, request)) { /* try next passdb */ auth_request_verify_plain(request, request->mech_password, @@ -949,7 +956,7 @@ void auth_request_verify_plain_continue(struct auth_request *request, struct auth_passdb *passdb; enum passdb_result result; - const char *cache_key; + const char *cache_key, *error; const char *password = request->mech_password; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); @@ -987,8 +994,13 @@ void auth_request_verify_plain_continue(struct auth_request *request, PASSDB_RESULT_INTERNAL_FAILURE, request); } else if (passdb->passdb->blocking) { passdb_blocking_verify_plain(request); + } else if (passdb_template_export(passdb->default_fields_tmpl, + request, &error) < 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand default_fields: %s", error); + auth_request_verify_plain_callback( + PASSDB_RESULT_INTERNAL_FAILURE, request); } else { - passdb_template_export(passdb->default_fields_tmpl, request); passdb->passdb->iface.verify_plain(request, password, auth_request_verify_plain_callback); } @@ -1000,7 +1012,14 @@ auth_request_lookup_credentials_finish(enum passdb_result result, size_t size, struct auth_request *request) { - passdb_template_export(request->passdb->override_fields_tmpl, request); + const char *error; + + if (passdb_template_export(request->passdb->override_fields_tmpl, + request, &error) < 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand override_fields: %s", error); + result = PASSDB_RESULT_INTERNAL_FAILURE; + } if (!auth_request_handle_passdb_callback(&result, request)) { /* try next passdb */ if (request->skip_password_check && @@ -1113,7 +1132,7 @@ void auth_request_lookup_credentials_policy_continue(struct auth_request *reques lookup_credentials_callback_t *callback) { struct auth_passdb *passdb; - const char *cache_key, *cache_cred, *cache_scheme; + const char *cache_key, *cache_cred, *cache_scheme, *error; enum passdb_result result; i_assert(request->state == AUTH_REQUEST_STATE_MECH_CONTINUE); @@ -1150,8 +1169,14 @@ void auth_request_lookup_credentials_policy_continue(struct auth_request *reques uchar_empty_ptr, 0, request); } else if (passdb->passdb->blocking) { passdb_blocking_lookup_credentials(request); + } else if (passdb_template_export(passdb->default_fields_tmpl, + request, &error) < 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand default_fields: %s", error); + auth_request_lookup_credentials_callback( + PASSDB_RESULT_INTERNAL_FAILURE, + uchar_empty_ptr, 0, request); } else { - passdb_template_export(passdb->default_fields_tmpl, request); passdb->passdb->iface.lookup_credentials(request, auth_request_lookup_credentials_callback); } @@ -1266,6 +1291,7 @@ void auth_request_userdb_callback(enum userdb_result result, struct auth_userdb *userdb = request->userdb; struct auth_userdb *next_userdb; enum auth_db_rule result_rule; + const char *error; bool userdb_continue = FALSE; switch (result) { @@ -1317,7 +1343,14 @@ void auth_request_userdb_callback(enum userdb_result result, if (result == USERDB_RESULT_OK) { /* this userdb lookup succeeded, preserve its extra fields */ - userdb_template_export(userdb->override_fields_tmpl, request); + if (userdb_template_export(userdb->override_fields_tmpl, + request, &error) < 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand override_fields: %s", error); + request->private_callback.userdb( + USERDB_RESULT_INTERNAL_FAILURE, request); + return; + } auth_fields_snapshot(request->userdb_reply); } else { /* this userdb lookup failed, remove any extra fields @@ -1332,8 +1365,14 @@ void auth_request_userdb_callback(enum userdb_result result, } if (request->userdb_success) { - result = USERDB_RESULT_OK; - userdb_template_export(userdb->override_fields_tmpl, request); + if (userdb_template_export(userdb->override_fields_tmpl, + request, &error) < 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand override_fields: %s", error); + result = USERDB_RESULT_INTERNAL_FAILURE; + } else { + result = USERDB_RESULT_OK; + } } else if (request->userdbs_seen_internal_failure || result == USERDB_RESULT_INTERNAL_FAILURE) { /* one of the userdb lookups failed. the user might have been @@ -1379,7 +1418,7 @@ void auth_request_lookup_user(struct auth_request *request, userdb_callback_t *callback) { struct auth_userdb *userdb = request->userdb; - const char *cache_key; + const char *cache_key, *error; request->private_callback.userdb = callback; request->userdb_lookup = TRUE; @@ -1390,7 +1429,14 @@ void auth_request_lookup_user(struct auth_request *request, /* we still want to set default_fields. these override any existing fields set by previous userdbs (because if that is unwanted, ":protected" can be used). */ - userdb_template_export(userdb->default_fields_tmpl, request); + if (userdb_template_export(userdb->default_fields_tmpl, + request, &error) < 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand default_fields: %s", error); + auth_request_userdb_callback( + USERDB_RESULT_INTERNAL_FAILURE, request); + return; + } } /* (for now) auth_cache is shared between passdb and userdb */ @@ -1448,6 +1494,7 @@ auth_request_fix_username(struct auth_request *request, const char *username, /* username format given, put it through variable expansion. we'll have to temporarily replace request->user to get %u to be the wanted username */ + const char *error; char *old_username; string_t *dest; @@ -1455,7 +1502,12 @@ auth_request_fix_username(struct auth_request *request, const char *username, request->user = user; dest = t_str_new(256); - auth_request_var_expand(dest, set->username_format, request, NULL); + if (auth_request_var_expand(dest, set->username_format, + request, NULL, &error) <= 0) { + *error_r = t_strdup_printf( + "Failed to expand username_format=%s: %s", + set->username_format, error); + } user = p_strdup(request->pool, str_c(dest)); request->user = old_username; @@ -1858,8 +1910,14 @@ void auth_request_set_fields(struct auth_request *request, void auth_request_init_userdb_reply(struct auth_request *request) { + const char *error; + request->userdb_reply = auth_fields_init(request->pool); - userdb_template_export(request->userdb->default_fields_tmpl, request); + if (userdb_template_export(request->userdb->default_fields_tmpl, + request, &error) <= 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand default_fields: %s", error); + } } static void auth_request_set_uidgid_file(struct auth_request *request, @@ -1867,10 +1925,15 @@ static void auth_request_set_uidgid_file(struct auth_request *request, { string_t *path; struct stat st; + const char *error; path = t_str_new(256); - auth_request_var_expand(path, path_template, request, NULL); - if (stat(str_c(path), &st) < 0) { + if (auth_request_var_expand(path, path_template, request, + NULL, &error) <= 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand uidgid_file=%s: %s", path_template, error); + request->userdb_lookup_tempfailed = TRUE; + } else if (stat(str_c(path), &st) < 0) { auth_request_log_error(request, AUTH_SUBSYS_DB, "stat(%s) failed: %m", str_c(path)); request->userdb_lookup_tempfailed = TRUE; diff --git a/src/auth/db-checkpassword.c b/src/auth/db-checkpassword.c index 79ba1373fc..b433d51191 100644 --- a/src/auth/db-checkpassword.c +++ b/src/auth/db-checkpassword.c @@ -294,9 +294,14 @@ checkpassword_get_cmd(struct auth_request *request, const char *args, const char *checkpassword_reply_path) { string_t *str; + const char *error; str = t_str_new(256); - auth_request_var_expand(str, args, request, NULL); + if (auth_request_var_expand(str, args, request, NULL, &error) <= 0) { + i_error("Failed to expand checkpassword_path=%s: %s", + args, error); + } + return t_strconcat(str_c(str), " ", checkpassword_reply_path, NULL); } diff --git a/src/auth/db-dict.c b/src/auth/db-dict.c index 9cd60d0f83..c10a5e3f25 100644 --- a/src/auth/db-dict.c +++ b/src/auth/db-dict.c @@ -413,7 +413,12 @@ static int db_dict_iter_lookup_key_values(struct db_dict_value_iter *iter) continue; str_truncate(path, strlen(DICT_PATH_SHARED)); - var_expand(path, key->key->key, iter->var_expand_table); + ret = var_expand(path, key->key->key, iter->var_expand_table, &error); + if (ret <= 0) { + auth_request_log_error(iter->auth_request, AUTH_SUBSYS_DB, + "Failed to expand key %s: %s", key->key->key, error); + return -1; + } ret = dict_lookup(iter->conn->dict, iter->pool, str_c(path), &key->value, &error); if (ret > 0) { @@ -465,9 +470,15 @@ int db_dict_value_iter_init(struct dict_connection *conn, struct db_dict_key *new_key = p_new(iter->pool, struct db_dict_key, 1); memcpy(new_key, key, sizeof(struct db_dict_key)); string_t *expanded_key = str_new(iter->pool, strlen(key->key)); - auth_request_var_expand_with_table(expanded_key, key->key, auth_request, - iter->var_expand_table, - NULL); + const char *error; + if (auth_request_var_expand_with_table(expanded_key, key->key, auth_request, + iter->var_expand_table, + NULL, &error) <= 0) { + auth_request_log_error(iter->auth_request, AUTH_SUBSYS_DB, + "Failed to expand key %s: %s", key->key, error); + pool_unref(&pool); + return -1; + } new_key->key = str_c(expanded_key); iterkey->key = new_key; } @@ -600,14 +611,21 @@ bool db_dict_value_iter_next(struct db_dict_value_iter *iter, { NULL, NULL } }; const struct db_dict_field *field; + const char *error; if (iter->field_idx == array_count(iter->fields)) return db_dict_value_iter_object_next(iter, key_r, value_r); field = array_idx(iter->fields, iter->field_idx++); str_truncate(iter->tmpstr, 0); - var_expand_with_funcs(iter->tmpstr, field->value, - iter->var_expand_table, var_funcs_table, iter); + if (var_expand_with_funcs(iter->tmpstr, field->value, + iter->var_expand_table, var_funcs_table, + iter, &error) <= 0) { + iter->error = p_strdup_printf(iter->pool, + "Failed to expand %s=%s: %s", + field->name, field->value, error); + return FALSE; + } *key_r = field->name; *value_r = str_c(iter->tmpstr); return TRUE; diff --git a/src/auth/db-ldap.c b/src/auth/db-ldap.c index 7374e0dca9..9fd4ac33f0 100644 --- a/src/auth/db-ldap.c +++ b/src/auth/db-ldap.c @@ -624,7 +624,7 @@ ldap_request_send_subquery(struct ldap_connection *conn, { NULL, NULL } }; const struct ldap_field *field; - const char *p; + const char *p, *error; char *name; struct ldap_field_find_subquery_context ctx; string_t *tmp_str = t_str_new(64); @@ -637,8 +637,14 @@ ldap_request_send_subquery(struct ldap_connection *conn, array_foreach(request->attr_map, field) { if (field->ldap_attr_name[0] == '\0') { str_truncate(tmp_str, 0); - var_expand_with_funcs(tmp_str, field->value, NULL, - var_funcs_table, &ctx); + if (var_expand_with_funcs(tmp_str, field->value, NULL, + var_funcs_table, &ctx, &error) <= 0) { + auth_request_log_error(request->request.auth_request, + AUTH_SUBSYS_DB, + "Failed to expand subquery %s: %s", + field->value, error); + return -1; + } } else { p = strchr(field->ldap_attr_name, '@'); if (p != NULL && @@ -1369,7 +1375,7 @@ void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist, struct ldap_field_find_context ctx; struct ldap_field *field; string_t *tmp_str; - const char *const *attr, *attr_data, *p; + const char *const *attr, *attr_data, *p, *error; char *ldap_attr, *name, *templ; unsigned int i; @@ -1406,8 +1412,11 @@ void db_ldap_set_attrs(struct ldap_connection *conn, const char *attrlist, } else { *templ++ = '\0'; str_truncate(tmp_str, 0); - var_expand_with_funcs(tmp_str, templ, NULL, - var_funcs_table, &ctx); + if (var_expand_with_funcs(tmp_str, templ, NULL, + var_funcs_table, &ctx, &error) <= 0) { + i_error("LDAP %s: Failed to expand attr_names=%s: %s", + conn->config_path, name, error); + } if (strchr(templ, '%') == NULL) { /* backwards compatibility: attr=name=prefix means same as @@ -1667,7 +1676,7 @@ db_ldap_result_return_value(struct db_ldap_result_iterate_context *ctx, struct db_ldap_value *ldap_value) { const struct var_expand_table *var_table; - const char *const *values; + const char *const *values, *error; if (ldap_value != NULL) values = ldap_value->values; @@ -1699,8 +1708,12 @@ db_ldap_result_return_value(struct db_ldap_result_iterate_context *ctx, (and less importantly the same for other variables) */ var_table = db_ldap_value_get_var_expand_table(ctx->auth_request, values[0]); - var_expand_with_funcs(ctx->var, field->value, var_table, - ldap_var_funcs_table, ctx); + if (var_expand_with_funcs(ctx->var, field->value, var_table, + ldap_var_funcs_table, ctx, &error) <= 0) { + auth_request_log_warning(ctx->auth_request, AUTH_SUBSYS_DB, + "Failed to expand template %s: %s", + field->value, error); + } ctx->val_1_arr[0] = str_c(ctx->var); values = ctx->val_1_arr; } @@ -1711,9 +1724,11 @@ bool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx, const char **name_r, const char *const **values_r) { + const struct var_expand_table *tab; const struct ldap_field *field; struct db_ldap_value *ldap_value; unsigned int pos; + const char *error; do { if (ctx->attr_idx == array_count(ctx->attr_map)) @@ -1740,9 +1755,12 @@ bool db_ldap_result_iterate_next(struct db_ldap_result_iterate_context *ctx, str_append_c(ctx->var, '\0'); pos = str_len(ctx->var); - var_expand_with_funcs(ctx->var, field->name, - auth_request_get_var_expand_table(ctx->auth_request, NULL), - ldap_var_funcs_table, ctx); + tab = auth_request_get_var_expand_table(ctx->auth_request, NULL); + if (var_expand_with_funcs(ctx->var, field->name, tab, + ldap_var_funcs_table, ctx, &error) <= 0) { + auth_request_log_warning(ctx->auth_request, AUTH_SUBSYS_DB, + "Failed to expand %s: %s", field->name, error); + } *name_r = str_c(ctx->var) + pos; } diff --git a/src/auth/db-passwd-file.c b/src/auth/db-passwd-file.c index 819cfcf6cc..49fdde6582 100644 --- a/src/auth/db-passwd-file.c +++ b/src/auth/db-passwd-file.c @@ -349,10 +349,12 @@ db_passwd_file_init(const char *path, bool userdb, bool debug) /* just extra escaped % chars. remove them. */ struct var_expand_table empty_table[1]; string_t *dest; + const char *error; empty_table[0].key = '\0'; dest = t_str_new(256); - var_expand(dest, path, empty_table); + if (var_expand(dest, path, empty_table, &error) <= 0) + i_unreached(); path = str_c(dest); } @@ -435,12 +437,19 @@ int db_passwd_file_lookup(struct db_passwd_file *db, { struct passwd_file *pw; string_t *username, *dest; + const char *error; if (!db->vars) pw = db->default_file; else { dest = t_str_new(256); - auth_request_var_expand(dest, db->path, request, path_fix); + if (auth_request_var_expand(dest, db->path, request, path_fix, + &error) <= 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand passwd-file path %s: %s", + db->path, error); + return -1; + } pw = hash_table_lookup(db->files, str_c(dest)); if (pw == NULL) { @@ -455,8 +464,13 @@ int db_passwd_file_lookup(struct db_passwd_file *db, } username = t_str_new(256); - auth_request_var_expand(username, username_format, request, - auth_request_str_escape); + if (auth_request_var_expand(username, username_format, request, + auth_request_str_escape, &error) <= 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand username_format=%s: %s", + username_format, error); + return -1; + } auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup: user=%s file=%s", diff --git a/src/auth/passdb-imap.c b/src/auth/passdb-imap.c index bbce9020eb..9a5a1b7bd9 100644 --- a/src/auth/passdb-imap.c +++ b/src/auth/passdb-imap.c @@ -76,6 +76,7 @@ passdb_imap_verify_plain(struct auth_request *auth_request, (struct imap_passdb_module *)_module; struct imap_auth_request *request; struct imapc_client_settings set; + const char *error; string_t *str; set = module->set; @@ -88,11 +89,25 @@ passdb_imap_verify_plain(struct auth_request *auth_request, if (module->set_have_vars) { str = t_str_new(128); - auth_request_var_expand(str, set.username, auth_request, NULL); + if (auth_request_var_expand(str, set.username, auth_request, + NULL, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand username=%s: %s", + set.username, error); + callback(PASSDB_RESULT_INTERNAL_FAILURE, auth_request); + return; + } set.username = t_strdup(str_c(str)); str_truncate(str, 0); - auth_request_var_expand(str, set.host, auth_request, NULL); + if (auth_request_var_expand(str, set.host, auth_request, + NULL, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand host=%s: %s", + set.host, error); + callback(PASSDB_RESULT_INTERNAL_FAILURE, auth_request); + return; + } set.host = t_strdup(str_c(str)); } auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, diff --git a/src/auth/passdb-ldap.c b/src/auth/passdb-ldap.c index 2f876df87e..65b0d1d26a 100644 --- a/src/auth/passdb-ldap.c +++ b/src/auth/passdb-ldap.c @@ -203,6 +203,20 @@ static void ldap_auth_bind(struct ldap_connection *conn, db_ldap_request(conn, &brequest->request); } +static void passdb_ldap_request_fail(struct passdb_ldap_request *request, + enum passdb_result passdb_result) +{ + struct auth_request *auth_request = request->request.ldap.auth_request; + + if (auth_request->credentials_scheme != NULL) { + request->callback.lookup_credentials(passdb_result, NULL, 0, + auth_request); + } else { + request->callback.verify_plain(passdb_result, auth_request); + } + auth_request_unref(&auth_request); +} + static void ldap_bind_lookup_dn_fail(struct auth_request *auth_request, struct passdb_ldap_request *request, @@ -222,13 +236,7 @@ ldap_bind_lookup_dn_fail(struct auth_request *auth_request, passdb_result = PASSDB_RESULT_INTERNAL_FAILURE; } - if (auth_request->credentials_scheme != NULL) { - request->callback.lookup_credentials(passdb_result, NULL, 0, - auth_request); - } else { - request->callback.verify_plain(passdb_result, auth_request); - } - auth_request_unref(&auth_request); + passdb_ldap_request_fail(request, passdb_result); } static void ldap_bind_lookup_dn_callback(struct ldap_connection *conn, @@ -288,18 +296,31 @@ static void ldap_lookup_pass(struct auth_request *auth_request, struct ldap_connection *conn = module->conn; struct ldap_request_search *srequest = &request->request.search; const char **attr_names = (const char **)conn->pass_attr_names; + const char *error; string_t *str; request->require_password = require_password; srequest->request.type = LDAP_REQUEST_TYPE_SEARCH; str = t_str_new(512); - auth_request_var_expand(str, conn->set.base, auth_request, ldap_escape); + if (auth_request_var_expand(str, conn->set.base, auth_request, + ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand base=%s: %s", conn->set.base, error); + passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE); + return; + } srequest->base = p_strdup(auth_request->pool, str_c(str)); str_truncate(str, 0); - auth_request_var_expand(str, conn->set.pass_filter, - auth_request, ldap_escape); + if (auth_request_var_expand(str, conn->set.pass_filter, + auth_request, ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand pass_filter=%s: %s", + conn->set.pass_filter, error); + passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE); + return; + } srequest->filter = p_strdup(auth_request->pool, str_c(str)); srequest->attr_map = &conn->pass_attr_map; srequest->attributes = conn->pass_attr_names; @@ -322,17 +343,30 @@ static void ldap_bind_lookup_dn(struct auth_request *auth_request, (struct ldap_passdb_module *)_module; struct ldap_connection *conn = module->conn; struct ldap_request_search *srequest = &request->request.search; + const char *error; string_t *str; srequest->request.type = LDAP_REQUEST_TYPE_SEARCH; str = t_str_new(512); - auth_request_var_expand(str, conn->set.base, auth_request, ldap_escape); + if (auth_request_var_expand(str, conn->set.base, auth_request, + ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand base=%s: %s", conn->set.base, error); + passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE); + return; + } srequest->base = p_strdup(auth_request->pool, str_c(str)); str_truncate(str, 0); - auth_request_var_expand(str, conn->set.pass_filter, - auth_request, ldap_escape); + if (auth_request_var_expand(str, conn->set.pass_filter, + auth_request, ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand pass_filter=%s: %s", + conn->set.pass_filter, error); + passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE); + return; + } srequest->filter = p_strdup(auth_request->pool, str_c(str)); /* we don't need the attributes to perform authentication, but they @@ -359,11 +393,19 @@ ldap_verify_plain_auth_bind_userdn(struct auth_request *auth_request, struct ldap_connection *conn = module->conn; struct ldap_request_bind *brequest = &request->request.bind; string_t *dn; + const char *error; brequest->request.type = LDAP_REQUEST_TYPE_BIND; dn = t_str_new(512); - auth_request_var_expand(dn, conn->set.auth_bind_userdn, auth_request, ldap_escape); + if (auth_request_var_expand(dn, conn->set.auth_bind_userdn, + auth_request, ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand auth_bind_userdn=%s: %s", + conn->set.auth_bind_userdn, error); + passdb_ldap_request_fail(request, PASSDB_RESULT_INTERNAL_FAILURE); + return; + } brequest->dn = p_strdup(auth_request->pool, str_c(dn)); ldap_auth_bind(conn, brequest); diff --git a/src/auth/passdb-pam.c b/src/auth/passdb-pam.c index 9824431b29..ffddf39286 100644 --- a/src/auth/passdb-pam.c +++ b/src/auth/passdb-pam.c @@ -312,14 +312,21 @@ pam_verify_plain(struct auth_request *request, const char *password, struct passdb_module *_module = request->passdb->passdb; struct pam_passdb_module *module = (struct pam_passdb_module *)_module; enum passdb_result result; - const char *service; + const char *service, *error; if (module->requests_left > 0) { if (--module->requests_left == 0) worker_restart_request = TRUE; } - service = t_auth_request_var_expand(module->service_name, request, NULL); + if (t_auth_request_var_expand(module->service_name, request, NULL, + &service, &error) <= 0) { + auth_request_log_debug(request, AUTH_SUBSYS_DB, + "Failed to expand service %s: %s", + module->service_name, error); + callback(PASSDB_RESULT_INTERNAL_FAILURE, request); + return; + } auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup service=%s", service); diff --git a/src/auth/passdb-passwd-file.c b/src/auth/passdb-passwd-file.c index 8978fd2baa..d4a3aac0ed 100644 --- a/src/auth/passdb-passwd-file.c +++ b/src/auth/passdb-passwd-file.c @@ -17,12 +17,12 @@ struct passwd_file_passdb_module { const char *username_format; }; -static void +static int passwd_file_add_extra_fields(struct auth_request *request, char *const *fields) { string_t *str = t_str_new(512); const struct var_expand_table *table; - const char *key, *value; + const char *key, *value, *error; unsigned int i; table = auth_request_get_var_expand_table(request, NULL); @@ -32,8 +32,13 @@ passwd_file_add_extra_fields(struct auth_request *request, char *const *fields) if (value != NULL) { key = t_strdup_until(fields[i], value); str_truncate(str, 0); - auth_request_var_expand_with_table(str, value + 1, - request, table, NULL); + if (auth_request_var_expand_with_table(str, value + 1, + request, table, NULL, &error) <= 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand extra field %s: %s", + fields[i], error); + return -1; + } value = str_c(str); } else { key = fields[i]; @@ -41,12 +46,13 @@ passwd_file_add_extra_fields(struct auth_request *request, char *const *fields) } auth_request_set_field(request, key, value, NULL); } + return 0; } -static void passwd_file_save_results(struct auth_request *request, - const struct passwd_user *pu, - const char **crypted_pass_r, - const char **scheme_r) +static int passwd_file_save_results(struct auth_request *request, + const struct passwd_user *pu, + const char **crypted_pass_r, + const char **scheme_r) { *crypted_pass_r = pu->password != NULL ? pu->password : ""; *scheme_r = password_get_scheme(crypted_pass_r); @@ -57,8 +63,11 @@ static void passwd_file_save_results(struct auth_request *request, auth_request_set_field(request, "password", *crypted_pass_r, *scheme_r); - if (pu->extra_fields != NULL) - passwd_file_add_extra_fields(request, pu->extra_fields); + if (pu->extra_fields != NULL) { + if (passwd_file_add_extra_fields(request, pu->extra_fields) < 0) + return -1; + } + return 0; } static void @@ -80,7 +89,10 @@ passwd_file_verify_plain(struct auth_request *request, const char *password, return; } - passwd_file_save_results(request, pu, &crypted_pass, &scheme); + if (passwd_file_save_results(request, pu, &crypted_pass, &scheme) < 0) { + callback(PASSDB_RESULT_INTERNAL_FAILURE, request); + return; + } ret = auth_request_password_verify(request, password, crypted_pass, scheme, AUTH_SUBSYS_DB); @@ -108,7 +120,10 @@ passwd_file_lookup_credentials(struct auth_request *request, return; } - passwd_file_save_results(request, pu, &crypted_pass, &scheme); + if (passwd_file_save_results(request, pu, &crypted_pass, &scheme) < 0) { + callback(PASSDB_RESULT_INTERNAL_FAILURE, NULL, 0, request); + return; + } passdb_handle_credentials(PASSDB_RESULT_OK, crypted_pass, scheme, callback, request); diff --git a/src/auth/passdb-sql.c b/src/auth/passdb-sql.c index 1ad1cae7f4..6440e43861 100644 --- a/src/auth/passdb-sql.c +++ b/src/auth/passdb-sql.c @@ -154,11 +154,18 @@ static void sql_lookup_pass(struct passdb_sql_request *sql_request) struct passdb_module *_module = sql_request->auth_request->passdb->passdb; struct sql_passdb_module *module = (struct sql_passdb_module *)_module; - const char *query; - - query = t_auth_request_var_expand(module->conn->set.password_query, - sql_request->auth_request, - passdb_sql_escape); + const char *query, *error; + + if (t_auth_request_var_expand(module->conn->set.password_query, + sql_request->auth_request, + passdb_sql_escape, &query, &error) <= 0) { + auth_request_log_debug(sql_request->auth_request, AUTH_SUBSYS_DB, + "Failed to expand password_query=%s: %s", + module->conn->set.password_query, error); + sql_request->callback.verify_plain(PASSDB_RESULT_INTERNAL_FAILURE, + sql_request->auth_request); + return; + } auth_request_log_debug(sql_request->auth_request, AUTH_SUBSYS_DB, "query: %s", query); @@ -227,12 +234,19 @@ static void sql_set_credentials(struct auth_request *request, (struct sql_passdb_module *) request->passdb->passdb; struct sql_transaction_context *transaction; struct passdb_sql_request *sql_request; - const char *query; + const char *query, *error; request->mech_password = p_strdup(request->pool, new_credentials); - query = t_auth_request_var_expand(module->conn->set.update_query, - request, passdb_sql_escape); + if (t_auth_request_var_expand(module->conn->set.update_query, + request, passdb_sql_escape, + &query, &error) <= 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand update_query=%s: %s", + module->conn->set.update_query, error); + callback(FALSE, request); + return; + } sql_request = i_new(struct passdb_sql_request, 1); sql_request->auth_request = request; diff --git a/src/auth/passdb-static.c b/src/auth/passdb-static.c index 631b967c89..0b6fee4b3f 100644 --- a/src/auth/passdb-static.c +++ b/src/auth/passdb-static.c @@ -12,17 +12,28 @@ struct static_passdb_module { }; static enum passdb_result -static_save_fields(struct auth_request *request, const char **password_r, const char **scheme_r) +static_save_fields(struct auth_request *request, const char **password_r, + const char **scheme_r) { struct static_passdb_module *module = (struct static_passdb_module *)request->passdb->passdb; + const char *error; auth_request_log_debug(request, AUTH_SUBSYS_DB, "lookup"); - passdb_template_export(module->tmpl, request); + if (passdb_template_export(module->tmpl, request, &error) < 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand template: %s", error); + return PASSDB_RESULT_INTERNAL_FAILURE; + } if (module->static_password_tmpl != NULL) { - *password_r = t_auth_request_var_expand( - module->static_password_tmpl, request, NULL); + if (t_auth_request_var_expand(module->static_password_tmpl, + request, NULL, password_r, &error) <= 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand password=%s: %s", + module->static_password_tmpl, error); + return PASSDB_RESULT_INTERNAL_FAILURE; + } } else if (auth_fields_exists(request->extra_fields, "nopassword")) { *password_r = ""; } else { diff --git a/src/auth/passdb-template.c b/src/auth/passdb-template.c index 6153af4111..143d3ab158 100644 --- a/src/auth/passdb-template.c +++ b/src/auth/passdb-template.c @@ -35,8 +35,9 @@ struct passdb_template *passdb_template_build(pool_t pool, const char *args) return tmpl; } -void passdb_template_export(struct passdb_template *tmpl, - struct auth_request *auth_request) +int passdb_template_export(struct passdb_template *tmpl, + struct auth_request *auth_request, + const char **error_r) { const struct var_expand_table *table; string_t *str; @@ -44,7 +45,7 @@ void passdb_template_export(struct passdb_template *tmpl, unsigned int i, count; if (passdb_template_is_empty(tmpl)) - return; + return 0; str = t_str_new(256); table = auth_request_get_var_expand_table(auth_request, NULL); @@ -56,13 +57,15 @@ void passdb_template_export(struct passdb_template *tmpl, value = ""; else { str_truncate(str, 0); - auth_request_var_expand_with_table(str, args[i+1], - auth_request, table, NULL); + if (auth_request_var_expand_with_table(str, args[i+1], + auth_request, table, NULL, error_r) <= 0) + return -1; value = str_c(str); } auth_request_set_field(auth_request, args[i], value, STATIC_PASS_SCHEME); } + return 0; } bool passdb_template_remove(struct passdb_template *tmpl, diff --git a/src/auth/passdb-template.h b/src/auth/passdb-template.h index b4a1f00357..df03e33c00 100644 --- a/src/auth/passdb-template.h +++ b/src/auth/passdb-template.h @@ -4,8 +4,9 @@ #define STATIC_PASS_SCHEME "PLAIN" struct passdb_template *passdb_template_build(pool_t pool, const char *args); -void passdb_template_export(struct passdb_template *tmpl, - struct auth_request *auth_request); +int passdb_template_export(struct passdb_template *tmpl, + struct auth_request *auth_request, + const char **error_r); bool passdb_template_remove(struct passdb_template *tmpl, const char *key, const char **value_r); bool passdb_template_is_empty(struct passdb_template *tmpl); diff --git a/src/auth/test-auth-cache.c b/src/auth/test-auth-cache.c index 14f4740194..134dad4850 100644 --- a/src/auth/test-auth-cache.c +++ b/src/auth/test-auth-cache.c @@ -18,14 +18,15 @@ const struct var_expand_table auth_request_var_expand_static_tab[] = { { '\0', NULL, NULL } }; -const char * -t_auth_request_var_expand(const char *str, - const struct auth_request *auth_request ATTR_UNUSED, - auth_request_escape_func_t *escape_func ATTR_UNUSED) +int t_auth_request_var_expand(const char *str, + const struct auth_request *auth_request ATTR_UNUSED, + auth_request_escape_func_t *escape_func ATTR_UNUSED, + const char **value_r, const char **error_r) { string_t *dest = t_str_new(128); - var_expand(dest, str, auth_request_var_expand_static_tab); - return str_c(dest); + int ret = var_expand(dest, str, auth_request_var_expand_static_tab, error_r); + *value_r = str_c(dest); + return ret; } static void test_auth_cache_parse_key(void) diff --git a/src/auth/test-auth-request-var-expand.c b/src/auth/test-auth-request-var-expand.c index 801bf04274..bf4e3a767d 100644 --- a/src/auth/test-auth-request-var-expand.c +++ b/src/auth/test-auth-request-var-expand.c @@ -71,9 +71,12 @@ test_escape(const char *string, const struct auth_request *request) static bool test_empty_request(string_t *str, const char *input) { + const struct var_expand_table *tab = + auth_request_get_var_expand_table(&empty_test_request, NULL); + const char *error; + str_truncate(str, 0); - var_expand(str, input, - auth_request_get_var_expand_table(&empty_test_request, NULL)); + test_assert(var_expand(str, input, tab, &error) == 1); return strspn(str_c(str), "\n0") == str_len(str); } @@ -90,17 +93,18 @@ static void test_auth_request_var_expand_shortlong(void) "+user@+domain1@+domain2\n+user\n+domain1@+domain2\n+service\n\n" "7.91.205.21\n73.150.2.210\n54321\n+password\n+mech\nsecured\n" "21\n210\nvalid\n"; + const struct var_expand_table *tab; string_t *str = t_str_new(256); + const char *error; test_begin("auth request var expand short and long"); - var_expand(str, test_input_short, - auth_request_get_var_expand_table(&test_request, test_escape)); + tab = auth_request_get_var_expand_table(&test_request, test_escape); + test_assert(var_expand(str, test_input_short, tab, &error) == 1); test_assert(strcmp(str_c(str), test_output) == 0); str_truncate(str, 0); - var_expand(str, test_input_long, - auth_request_get_var_expand_table(&test_request, test_escape)); + test_assert(var_expand(str, test_input_long, tab, &error) == 1); test_assert(strcmp(str_c(str), test_output) == 0); /* test with empty input that it won't crash */ @@ -114,14 +118,16 @@ static void test_auth_request_var_expand_flags(void) { static const char *test_input = "%!\n%{secured}\n%{cert}\n"; string_t *str = t_str_new(10); + const char *error; test_begin("auth request var expand flags"); test_request.userdb_lookup = FALSE; test_request.secured = FALSE; test_request.valid_client_cert = FALSE; - var_expand(str, test_input, - auth_request_get_var_expand_table(&test_request, test_escape)); + test_assert(var_expand(str, test_input, + auth_request_get_var_expand_table(&test_request, test_escape), + &error) == 1); test_assert(strcmp(str_c(str), "40\n\n\n") == 0); test_request.userdb_lookup = TRUE; @@ -129,8 +135,9 @@ static void test_auth_request_var_expand_flags(void) test_request.valid_client_cert = TRUE; str_truncate(str, 0); - var_expand(str, test_input, - auth_request_get_var_expand_table(&test_request, test_escape)); + test_assert(var_expand(str, test_input, + auth_request_get_var_expand_table(&test_request, test_escape), + &error) == 1); test_assert(strcmp(str_c(str), "41\nsecured\nvalid\n") == 0); test_assert(test_empty_request(str, test_input)); @@ -150,11 +157,13 @@ static void test_auth_request_var_expand_long(void) "+masteruser@+masterdomain1@+masterdomain2\n5000\n" "+origuser@+origdomain1@+origdomain2\n+origuser\n+origdomain1@+origdomain2\n"; string_t *str = t_str_new(256); + const char *error; test_begin("auth request var expand long-only"); - var_expand(str, test_input, - auth_request_get_var_expand_table(&test_request, test_escape)); + test_assert(var_expand(str, test_input, + auth_request_get_var_expand_table(&test_request, test_escape), + &error) == 1); test_assert(strcmp(str_c(str), test_output) == 0); test_assert(test_empty_request(str, test_input)); @@ -173,14 +182,16 @@ static void test_auth_request_var_expand_usernames(void) static const char *test_input = "%{username}\n%{domain}\n%{domain_first}\n%{domain_last}\n%{user}"; string_t *str = t_str_new(64); + const char *error; unsigned int i; test_begin("auth request var expand usernames"); for (i = 0; i < N_ELEMENTS(tests); i++) { test_request.user = t_strdup_noconst(tests[i].username); str_truncate(str, 0); - var_expand(str, test_input, - auth_request_get_var_expand_table(&test_request, test_escape)); + test_assert(var_expand(str, test_input, + auth_request_get_var_expand_table(&test_request, test_escape), + &error) == 1); test_assert_idx(strcmp(str_c(str), tests[i].output) == 0, i); } test_request.user = default_test_request.user; @@ -190,7 +201,7 @@ static void test_auth_request_var_expand_usernames(void) static void test_auth_request_var_expand_funcs(void) { pool_t pool; - const char *value; + const char *value, *error; test_begin("auth request var expand funcs"); @@ -204,20 +215,20 @@ static void test_auth_request_var_expand_funcs(void) auth_fields_add(test_request.userdb_reply, "ukey1", "-uval1", 0); auth_fields_add(test_request.userdb_reply, "ukey2", "", 0); - value = t_auth_request_var_expand( + test_assert(t_auth_request_var_expand( "%{passdb:pkey1}\n%{passdb:pkey1:default1}\n" "%{passdb:pkey2}\n%{passdb:pkey2:default2}\n" "%{passdb:pkey3}\n%{passdb:pkey3:default3}\n" "%{passdb:ukey1}\n%{passdb:ukey1:default4}\n", - &test_request, test_escape); + &test_request, test_escape, &value, &error) == 0); test_assert(strcmp(value, "+pval1\n+pval1\n\n\n\ndefault3\n\ndefault4\n") == 0); - value = t_auth_request_var_expand( + test_assert(t_auth_request_var_expand( "%{userdb:ukey1}\n%{userdb:ukey1:default1}\n" "%{userdb:ukey2}\n%{userdb:ukey2:default2}\n" "%{userdb:ukey3}\n%{userdb:ukey3:default3}\n" "%{userdb:pkey1}\n%{userdb:pkey1:default4}\n", - &test_request, test_escape); + &test_request, test_escape, &value, &error) == 0); test_assert(strcmp(value, "+uval1\n+uval1\n\n\n\ndefault3\n\ndefault4\n") == 0); pool_unref(&pool); diff --git a/src/auth/userdb-dict.c b/src/auth/userdb-dict.c index 90ddb400a4..f594b57a99 100644 --- a/src/auth/userdb-dict.c +++ b/src/auth/userdb-dict.c @@ -88,6 +88,7 @@ userdb_dict_iterate_init(struct auth_request *auth_request, (struct dict_userdb_module *)_module; struct dict_userdb_iterate_context *ctx; string_t *path; + const char *error; ctx = i_new(struct dict_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; @@ -106,8 +107,14 @@ userdb_dict_iterate_init(struct auth_request *auth_request, path = t_str_new(128); str_append(path, DICT_PATH_SHARED); - auth_request_var_expand(path, module->conn->set.iterate_prefix, - auth_request, NULL); + if (auth_request_var_expand(path, module->conn->set.iterate_prefix, + auth_request, NULL, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand iterate_prefix=%s: %s", + module->conn->set.iterate_prefix, error); + ctx->ctx.failed = TRUE; + return &ctx->ctx; + } ctx->key_prefix = p_strdup(auth_request->pool, str_c(path)); ctx->key_prefix_len = strlen(ctx->key_prefix); diff --git a/src/auth/userdb-ldap.c b/src/auth/userdb-ldap.c index 670f3e6571..ad73187fe3 100644 --- a/src/auth/userdb-ldap.c +++ b/src/auth/userdb-ldap.c @@ -110,6 +110,7 @@ static void userdb_ldap_lookup(struct auth_request *auth_request, struct ldap_connection *conn = module->conn; const char **attr_names = (const char **)conn->user_attr_names; struct userdb_ldap_request *request; + const char *error; string_t *str; auth_request_ref(auth_request); @@ -117,11 +118,24 @@ static void userdb_ldap_lookup(struct auth_request *auth_request, request->userdb_callback = callback; str = t_str_new(512); - auth_request_var_expand(str, conn->set.base, auth_request, ldap_escape); + if (auth_request_var_expand(str, conn->set.base, auth_request, + ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand base=%s: %s", conn->set.base, error); + callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); + return; + } request->request.base = p_strdup(auth_request->pool, str_c(str)); str_truncate(str, 0); - auth_request_var_expand(str, conn->set.user_filter, auth_request, ldap_escape); + if (auth_request_var_expand(str, conn->set.user_filter, auth_request, + ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand user_filter=%s: %s", + conn->set.user_filter, error); + callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); + return; + } request->request.filter = p_strdup(auth_request->pool, str_c(str)); request->request.attr_map = &conn->user_attr_map; @@ -191,6 +205,7 @@ userdb_ldap_iterate_init(struct auth_request *auth_request, struct ldap_userdb_iterate_context *ctx; struct userdb_iter_ldap_request *request; const char **attr_names = (const char **)conn->iterate_attr_names; + const char *error; string_t *str; ctx = i_new(struct ldap_userdb_iterate_context, 1); @@ -205,12 +220,22 @@ userdb_ldap_iterate_init(struct auth_request *auth_request, request->request.request.auth_request = auth_request; str = t_str_new(512); - auth_request_var_expand(str, conn->set.base, auth_request, ldap_escape); + if (auth_request_var_expand(str, conn->set.base, auth_request, + ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand base=%s: %s", conn->set.base, error); + ctx->ctx.failed = TRUE; + } request->request.base = p_strdup(auth_request->pool, str_c(str)); str_truncate(str, 0); - auth_request_var_expand(str, conn->set.iterate_filter, - auth_request, ldap_escape); + if (auth_request_var_expand(str, conn->set.iterate_filter, + auth_request, ldap_escape, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand iterate_filter=%s: %s", + conn->set.iterate_filter, error); + ctx->ctx.failed = TRUE; + } request->request.filter = p_strdup(auth_request->pool, str_c(str)); request->request.attr_map = &conn->iterate_attr_map; request->request.attributes = conn->iterate_attr_names; diff --git a/src/auth/userdb-passwd-file.c b/src/auth/userdb-passwd-file.c index f2e536c884..4da76858d6 100644 --- a/src/auth/userdb-passwd-file.c +++ b/src/auth/userdb-passwd-file.c @@ -27,12 +27,12 @@ struct passwd_file_userdb_module { const char *username_format; }; -static void +static int passwd_file_add_extra_fields(struct auth_request *request, char *const *fields) { string_t *str = t_str_new(512); const struct var_expand_table *table; - const char *key, *value; + const char *key, *value, *error; unsigned int i; table = auth_request_get_var_expand_table(request, NULL); @@ -46,14 +46,20 @@ passwd_file_add_extra_fields(struct auth_request *request, char *const *fields) if (value != NULL) { key = t_strdup_until(key, value); str_truncate(str, 0); - auth_request_var_expand_with_table(str, value + 1, - request, table, NULL); + if (auth_request_var_expand_with_table(str, value + 1, + request, table, NULL, &error) <= 0) { + auth_request_log_error(request, AUTH_SUBSYS_DB, + "Failed to expand extra field %s: %s", + fields[i], error); + return -1; + } value = str_c(str); } else { value = ""; } auth_request_set_userdb_field(request, key, value); } + return 0; } static void passwd_file_lookup(struct auth_request *auth_request, @@ -68,7 +74,8 @@ static void passwd_file_lookup(struct auth_request *auth_request, ret = db_passwd_file_lookup(module->pwf, auth_request, module->username_format, &pu); if (ret <= 0 || pu->uid == 0) { - callback(USERDB_RESULT_USER_UNKNOWN, auth_request); + callback(ret < 0 ? USERDB_RESULT_INTERNAL_FAILURE : + USERDB_RESULT_USER_UNKNOWN, auth_request); return; } @@ -84,8 +91,11 @@ static void passwd_file_lookup(struct auth_request *auth_request, if (pu->home != NULL) auth_request_set_userdb_field(auth_request, "home", pu->home); - if (pu->extra_fields != NULL) - passwd_file_add_extra_fields(auth_request, pu->extra_fields); + if (pu->extra_fields != NULL && + passwd_file_add_extra_fields(auth_request, pu->extra_fields) < 0) { + callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); + return; + } callback(USERDB_RESULT_OK, auth_request); } diff --git a/src/auth/userdb-passwd.c b/src/auth/userdb-passwd.c index a1f187140c..68154ac065 100644 --- a/src/auth/userdb-passwd.c +++ b/src/auth/userdb-passwd.c @@ -84,6 +84,7 @@ static void passwd_lookup(struct auth_request *auth_request, (struct passwd_userdb_module *)_module; struct passwd pw; struct timeval start_tv; + const char *error; int ret; auth_request_log_debug(auth_request, AUTH_SUBSYS_DB, "lookup"); @@ -114,7 +115,11 @@ static void passwd_lookup(struct auth_request *auth_request, auth_request_set_userdb_field(auth_request, "gid", dec2str(pw.pw_gid)); auth_request_set_userdb_field(auth_request, "home", pw.pw_dir); - userdb_template_export(module->tmpl, auth_request); + if (userdb_template_export(module->tmpl, auth_request, &error) < 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand template: %s", error); + callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); + } callback(USERDB_RESULT_OK, auth_request); } diff --git a/src/auth/userdb-sql.c b/src/auth/userdb-sql.c index 7bdd6b111c..a45c321523 100644 --- a/src/auth/userdb-sql.c +++ b/src/auth/userdb-sql.c @@ -105,10 +105,17 @@ static void userdb_sql_lookup(struct auth_request *auth_request, struct sql_userdb_module *module = (struct sql_userdb_module *)_module; struct userdb_sql_request *sql_request; - const char *query; - - query = t_auth_request_var_expand(module->conn->set.user_query, - auth_request, userdb_sql_escape); + const char *query, *error; + + if (t_auth_request_var_expand(module->conn->set.user_query, + auth_request, userdb_sql_escape, + &query, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand user_query=%s: %s", + module->conn->set.user_query, error); + callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); + return; + } auth_request_ref(auth_request); sql_request = i_new(struct userdb_sql_request, 1); @@ -141,10 +148,15 @@ userdb_sql_iterate_init(struct auth_request *auth_request, struct sql_userdb_module *module = (struct sql_userdb_module *)_module; struct sql_userdb_iterate_context *ctx; - const char *query; - - query = t_auth_request_var_expand(module->conn->set.iterate_query, - auth_request, userdb_sql_escape); + const char *query, *error; + + if (t_auth_request_var_expand(module->conn->set.iterate_query, + auth_request, userdb_sql_escape, + &query, &error) <= 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand iterate_query=%s: %s", + module->conn->set.iterate_query, error); + } ctx = i_new(struct sql_userdb_iterate_context, 1); ctx->ctx.auth_request = auth_request; diff --git a/src/auth/userdb-static.c b/src/auth/userdb-static.c index 5578512300..964484dbfc 100644 --- a/src/auth/userdb-static.c +++ b/src/auth/userdb-static.c @@ -27,8 +27,13 @@ static void static_lookup_real(struct auth_request *auth_request, struct userdb_module *_module = auth_request->userdb->userdb; struct static_userdb_module *module = (struct static_userdb_module *)_module; + const char *error; - userdb_template_export(module->tmpl, auth_request); + if (userdb_template_export(module->tmpl, auth_request, &error) < 0) { + auth_request_log_error(auth_request, AUTH_SUBSYS_DB, + "Failed to expand template: %s", error); + callback(USERDB_RESULT_INTERNAL_FAILURE, auth_request); + } callback(USERDB_RESULT_OK, auth_request); } diff --git a/src/auth/userdb-template.c b/src/auth/userdb-template.c index 840f303b03..f854b4b537 100644 --- a/src/auth/userdb-template.c +++ b/src/auth/userdb-template.c @@ -58,8 +58,9 @@ userdb_template_build(pool_t pool, const char *userdb_name, const char *args) return tmpl; } -void userdb_template_export(struct userdb_template *tmpl, - struct auth_request *auth_request) +int userdb_template_export(struct userdb_template *tmpl, + struct auth_request *auth_request, + const char **error_r) { const struct var_expand_table *table; string_t *str; @@ -67,7 +68,7 @@ void userdb_template_export(struct userdb_template *tmpl, unsigned int i, count; if (userdb_template_is_empty(tmpl)) - return; + return 0; str = t_str_new(256); table = auth_request_get_var_expand_table(auth_request, NULL); @@ -79,12 +80,14 @@ void userdb_template_export(struct userdb_template *tmpl, value = ""; else { str_truncate(str, 0); - auth_request_var_expand_with_table(str, args[i+1], - auth_request, table, NULL); + if (auth_request_var_expand_with_table(str, args[i+1], + auth_request, table, NULL, error_r) <= 0) + return -1; value = str_c(str); } auth_request_set_userdb_field(auth_request, args[i], value); } + return 0; } bool userdb_template_remove(struct userdb_template *tmpl, diff --git a/src/auth/userdb-template.h b/src/auth/userdb-template.h index 3af7452ef3..bf1ce8181d 100644 --- a/src/auth/userdb-template.h +++ b/src/auth/userdb-template.h @@ -3,8 +3,9 @@ struct userdb_template * userdb_template_build(pool_t pool, const char *userdb_name, const char *args); -void userdb_template_export(struct userdb_template *tmpl, - struct auth_request *auth_request); +int userdb_template_export(struct userdb_template *tmpl, + struct auth_request *auth_request, + const char **error_r); bool userdb_template_remove(struct userdb_template *tmpl, const char *key, const char **value_r); bool userdb_template_is_empty(struct userdb_template *tmpl); diff --git a/src/director/director-request.c b/src/director/director-request.c index c7c6d952c5..01aba8abf5 100644 --- a/src/director/director-request.c +++ b/src/director/director-request.c @@ -125,8 +125,13 @@ void director_request(struct director *dir, const char *username, director_request_callback *callback, void *context) { struct director_request *request; - unsigned int username_hash = - user_directory_get_username_hash(dir->users, username); + unsigned int username_hash; + + if (!user_directory_get_username_hash(dir->users, username, + &username_hash)) { + callback(NULL, NULL, "Failed to expand director_username_hash", context); + return; + } dir->num_requests++; diff --git a/src/director/director.c b/src/director/director.c index 3f7deb70ed..57ceb037a6 100644 --- a/src/director/director.c +++ b/src/director/director.c @@ -762,6 +762,7 @@ director_flush_user(struct director *dir, struct user *user) { 'h', user->host->hostname, "host" }, { '\0', NULL, NULL } }; + const char *error; /* Execute flush script, if set. Only the director that started the user moving will call the flush script. Having each director do it @@ -782,10 +783,14 @@ director_flush_user(struct director *dir, struct user *user) ctx->host_ip = user->host->ip; string_t *s_sock = str_new(default_pool, 32); - var_expand(s_sock, dir->set->director_flush_socket, tab); + if (var_expand(s_sock, dir->set->director_flush_socket, tab, &error) <= 0) { + i_error("Failed to expand director_flush_socket=%s: %s", + dir->set->director_flush_socket, error); + director_user_kill_finish_delayed(dir, user, FALSE); + return; + } ctx->socket_path = str_free_without_data(&s_sock); - const char *error; struct program_client_settings set = { .client_connect_timeout_msecs = 10000, .dns_client_socket_path = DIRECTOR_DNS_SOCKET_PATH, diff --git a/src/director/doveadm-connection.c b/src/director/doveadm-connection.c index cadca4054f..e2125820d1 100644 --- a/src/director/doveadm-connection.c +++ b/src/director/doveadm-connection.c @@ -564,8 +564,13 @@ doveadm_cmd_user_lookup(struct doveadm_connection *conn, username = args[0]; tag = args[1] != NULL ? args[1] : ""; } - if (str_to_uint(username, &username_hash) < 0) - username_hash = user_directory_get_username_hash(conn->dir->users, username); + if (str_to_uint(username, &username_hash) < 0) { + if (!user_directory_get_username_hash(conn->dir->users, + args[0], &username_hash)) { + o_stream_nsend_str(conn->output, "TRYAGAIN\n"); + return 1; + } + } /* get user's current host */ user = user_directory_lookup(conn->dir->users, username_hash); @@ -648,8 +653,13 @@ doveadm_cmd_user_move(struct doveadm_connection *conn, const char *const *args) return 1; } - if (str_to_uint(args[0], &username_hash) < 0) - username_hash = user_directory_get_username_hash(conn->dir->users, args[0]); + if (str_to_uint(args[0], &username_hash) < 0) { + if (!user_directory_get_username_hash(conn->dir->users, + args[0], &username_hash)) { + o_stream_nsend_str(conn->output, "TRYAGAIN\n"); + return 1; + } + } user = user_directory_lookup(conn->dir->users, username_hash); if (user != NULL && USER_IS_BEING_KILLED(user)) { o_stream_nsend_str(conn->output, "TRYAGAIN\n"); diff --git a/src/director/notify-connection.c b/src/director/notify-connection.c index b2cb8efae6..76ecfd3aa5 100644 --- a/src/director/notify-connection.c +++ b/src/director/notify-connection.c @@ -25,7 +25,9 @@ static void notify_connection_input(struct notify_connection *conn) int diff; while ((line = i_stream_read_next_line(conn->input)) != NULL) { - hash = user_directory_get_username_hash(conn->dir->users, line); + if (!user_directory_get_username_hash(conn->dir->users, line, &hash)) + continue; + user = user_directory_lookup(conn->dir->users, hash); if (user != NULL) { diff = ioloop_time - user->timestamp; diff --git a/src/director/test-user-directory.c b/src/director/test-user-directory.c index a392da369f..9b97a00a93 100644 --- a/src/director/test-user-directory.c +++ b/src/director/test-user-directory.c @@ -10,8 +10,13 @@ #define USER_DIR_TIMEOUT 1000000 -unsigned int mail_user_hash(const char *username ATTR_UNUSED, - const char *format ATTR_UNUSED) { return 0; } +bool mail_user_hash(const char *username ATTR_UNUSED, + const char *format ATTR_UNUSED, + unsigned int *hash_r, const char **error_r ATTR_UNUSED) +{ + *hash_r = 0; + return TRUE; +} static void verify_user_directory(struct user_directory *dir, unsigned int user_count) diff --git a/src/director/user-directory.c b/src/director/user-directory.c index 1e57e5784c..aa1c365855 100644 --- a/src/director/user-directory.c +++ b/src/director/user-directory.c @@ -256,10 +256,17 @@ void user_directory_sort(struct user_directory *dir) array_free(&users); } -unsigned int user_directory_get_username_hash(struct user_directory *dir, - const char *username) +bool user_directory_get_username_hash(struct user_directory *dir, + const char *username, + unsigned int *hash_r) { - return mail_user_hash(username, dir->username_hash_fmt); + const char *error; + + if (mail_user_hash(username, dir->username_hash_fmt, hash_r, &error)) + return TRUE; + i_error("Failed to expand director_user_expire=%s: %s", + dir->username_hash_fmt, error); + return FALSE; } bool user_directory_user_is_recently_updated(struct user_directory *dir, diff --git a/src/director/user-directory.h b/src/director/user-directory.h index 24538fa27f..0be4a8c829 100644 --- a/src/director/user-directory.h +++ b/src/director/user-directory.h @@ -52,8 +52,9 @@ void user_directory_remove_host(struct user_directory *dir, timestamps based on remote director's user list after handshake. */ void user_directory_sort(struct user_directory *dir); -unsigned int user_directory_get_username_hash(struct user_directory *dir, - const char *username); +bool user_directory_get_username_hash(struct user_directory *dir, + const char *username, + unsigned int *hash_r); bool user_directory_user_is_recently_updated(struct user_directory *dir, struct user *user); diff --git a/src/doveadm/doveadm-auth-server.c b/src/doveadm/doveadm-auth-server.c index 2b550da066..e9a5258162 100644 --- a/src/doveadm/doveadm-auth-server.c +++ b/src/doveadm/doveadm-auth-server.c @@ -327,18 +327,26 @@ cmd_user_mail_input(struct mail_storage_service_ctx *storage_service, cmd_user_mail_print_fields(input, user, userdb_fields, show_field); else { string_t *str = t_str_new(128); - var_expand_with_funcs(str, expand_field, - mail_user_var_expand_table(user), - mail_user_var_expand_func_table, user); - string_t *value = t_str_new(128); - json_append_escaped(value, expand_field); - o_stream_nsend_str(doveadm_print_ostream, "\""); - o_stream_nsend_str(doveadm_print_ostream, str_c(value)); - o_stream_nsend_str(doveadm_print_ostream, "\":\""); - str_truncate(value, 0); - json_append_escaped(value, str_c(str)); - o_stream_nsend_str(doveadm_print_ostream, str_c(value)); - o_stream_nsend_str(doveadm_print_ostream, "\""); + if (var_expand_with_funcs(str, expand_field, + mail_user_var_expand_table(user), + mail_user_var_expand_func_table, user, + &error) <= 0) { + string_t *str = t_str_new(128); + str_printfa(str, "\"error\":\"Failed to expand field: "); + json_append_escaped(str, error); + str_append_c(str, '"'); + o_stream_nsend(doveadm_print_ostream, str_data(str), str_len(str)); + } else { + string_t *value = t_str_new(128); + json_append_escaped(value, expand_field); + o_stream_nsend_str(doveadm_print_ostream, "\""); + o_stream_nsend_str(doveadm_print_ostream, str_c(value)); + o_stream_nsend_str(doveadm_print_ostream, "\":\""); + str_truncate(value, 0); + json_append_escaped(value, str_c(str)); + o_stream_nsend_str(doveadm_print_ostream, str_c(value)); + o_stream_nsend_str(doveadm_print_ostream, "\""); + } } diff --git a/src/doveadm/doveadm-auth.c b/src/doveadm/doveadm-auth.c index 064a986085..73812f4eaf 100644 --- a/src/doveadm/doveadm-auth.c +++ b/src/doveadm/doveadm-auth.c @@ -579,10 +579,14 @@ cmd_user_mail_input(struct mail_storage_service_ctx *storage_service, cmd_user_mail_print_fields(input, user, userdb_fields, show_field); else { string_t *str = t_str_new(128); - var_expand_with_funcs(str, expand_field, - mail_user_var_expand_table(user), - mail_user_var_expand_func_table, user); - printf("%s\n", str_c(str)); + if (var_expand_with_funcs(str, expand_field, + mail_user_var_expand_table(user), + mail_user_var_expand_func_table, user, + &error) <= 0) { + i_error("Failed to expand %s: %s", expand_field, error); + } else { + printf("%s\n", str_c(str)); + } } mail_user_unref(&user); diff --git a/src/doveadm/doveadm-director.c b/src/doveadm/doveadm-director.c index 4eee4a2b0b..9561456fc1 100644 --- a/src/doveadm/doveadm-director.c +++ b/src/doveadm/doveadm-director.c @@ -235,6 +235,19 @@ static void cmd_director_status(struct doveadm_cmd_context *cctx) director_disconnect(ctx); } +static bool user_hash_expand(const char *username, unsigned int *hash_r) +{ + const char *error; + + if (!mail_user_hash(username, doveadm_settings->director_username_hash, + hash_r, &error)) { + i_error("Failed to expand director_username_hash=%s: %s", + doveadm_settings->director_username_hash, error); + return FALSE; + } + return TRUE; +} + static void user_list_add(const char *username, pool_t pool, HASH_TABLE_TYPE(user_list) users) @@ -242,9 +255,11 @@ user_list_add(const char *username, pool_t pool, struct user_list *user, *old_user; unsigned int user_hash; + if (!user_hash_expand(username, &user_hash)) + return; + user = p_new(pool, struct user_list, 1); user->name = p_strdup(pool, username); - user_hash = mail_user_hash(username, doveadm_settings->director_username_hash); old_user = hash_table_lookup(users, POINTER_CAST(user_hash)); if (old_user != NULL) @@ -340,10 +355,11 @@ static void cmd_director_map(struct doveadm_cmd_context *cctx) if (ctx->user_map) { /* user -> hash mapping */ - user_hash = mail_user_hash(ctx->host, doveadm_settings->director_username_hash); - doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); - doveadm_print_header("hash", "hash", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); - doveadm_print(t_strdup_printf("%u", user_hash)); + if (user_hash_expand(ctx->host, &user_hash)) { + doveadm_print_init(DOVEADM_PRINT_TYPE_TABLE); + doveadm_print_header("hash", "hash", DOVEADM_PRINT_HEADER_FLAG_HIDE_TITLE); + doveadm_print(t_strdup_printf("%u", user_hash)); + } director_disconnect(ctx); return; } @@ -570,9 +586,8 @@ static void cmd_director_move(struct doveadm_cmd_context *cctx) return; } - user_hash = mail_user_hash(ctx->user, doveadm_settings->director_username_hash); - - if (director_get_host(ctx->host, &ips, &ips_count) != 0) { + if (!user_hash_expand(ctx->user, &user_hash) || + director_get_host(ctx->host, &ips, &ips_count) != 0) { director_disconnect(ctx); return; } diff --git a/src/doveadm/doveadm-dsync.c b/src/doveadm/doveadm-dsync.c index c55e59b7fd..fe144877c4 100644 --- a/src/doveadm/doveadm-dsync.c +++ b/src/doveadm/doveadm-dsync.c @@ -228,7 +228,7 @@ get_ssh_cmd_args(const char *host, const char *login, const char *mail_user) struct var_expand_table *tab; ARRAY_TYPE(const_string) cmd_args; string_t *str, *str2; - const char *value, *const *args; + const char *value, *const *args, *error; tab = t_malloc_no0(sizeof(static_tab)); memcpy(tab, static_tab, sizeof(static_tab)); @@ -250,8 +250,11 @@ get_ssh_cmd_args(const char *host, const char *login, const char *mail_user) text in the parameter, skip it. */ str_truncate(str, 0); str_truncate(str2, 0); - var_expand(str, *args, tab); - var_expand(str2, *args, static_tab); + if (var_expand(str, *args, tab, &error) <= 0 || + var_expand(str2, *args, static_tab, &error) <= 0) { + i_error("Failed to expand dsync_remote_cmd=%s: %s", + *args, error); + } if (strcmp(str_c(str), str_c(str2)) == 0 && str_len(str) > 0) continue; diff --git a/src/doveadm/doveadm-print-formatted.c b/src/doveadm/doveadm-print-formatted.c index 28347eb81d..4d844bbf43 100644 --- a/src/doveadm/doveadm-print-formatted.c +++ b/src/doveadm/doveadm-print-formatted.c @@ -56,11 +56,15 @@ static void doveadm_print_formatted_print(const char *value) if (ctx.format == NULL) { i_fatal("formatted formatter cannot be used without a format."); } + const char *error; struct var_expand_table *entry = array_idx_modifiable(&ctx.headers, ctx.idx++); entry->value = value; if (ctx.idx >= array_count(&ctx.headers)) { - var_expand(ctx.buf, ctx.format, array_idx(&ctx.headers,0)); + if (var_expand(ctx.buf, ctx.format, array_idx(&ctx.headers,0), &error) <= 0) { + i_error("Failed to expand print format '%s': %s", + ctx.format, error); + } doveadm_print_formatted_flush(); ctx.idx = 0; } diff --git a/src/imap-hibernate/imap-client.c b/src/imap-hibernate/imap-client.c index e177721ee8..5461ddce0d 100644 --- a/src/imap-hibernate/imap-client.c +++ b/src/imap-hibernate/imap-client.c @@ -506,7 +506,7 @@ imap_client_create(int fd, const struct imap_client_state *state) struct imap_client *client; pool_t pool = pool_alloconly_create("imap client", 256); void *statebuf; - const char *ident; + const char *ident, *error; i_assert(state->username != NULL); i_assert(state->mail_log_prefix != NULL); @@ -533,8 +533,12 @@ imap_client_create(int fd, const struct imap_client_state *state) string_t *str; str = t_str_new(256); - var_expand(str, state->mail_log_prefix, - imap_client_get_var_expand_table(client)); + if (var_expand(str, state->mail_log_prefix, + imap_client_get_var_expand_table(client), + &error) <= 0) { + i_error("Failed to expand mail_log_prefix=%s: %s", + state->mail_log_prefix, error); + } client->log_prefix = p_strdup(pool, str_c(str)); } T_END; diff --git a/src/imap-urlauth/imap-urlauth-worker.c b/src/imap-urlauth/imap-urlauth-worker.c index 6abb273d24..ed1824f46a 100644 --- a/src/imap-urlauth/imap-urlauth-worker.c +++ b/src/imap-urlauth/imap-urlauth-worker.c @@ -609,9 +609,15 @@ client_handle_user_command(struct client *client, const char *cmd, restrict_access_allow_coredumps(TRUE); set = mail_storage_service_user_get_set(user)[1]; - settings_var_expand(&imap_urlauth_worker_setting_parser_info, set, - mail_user->pool, - mail_user_var_expand_table(mail_user)); + if (settings_var_expand(&imap_urlauth_worker_setting_parser_info, set, + mail_user->pool, + mail_user_var_expand_table(mail_user), + &error) <= 0) { + client_send_line(client, "NO"); + client_abort(client, t_strdup_printf( + "Session aborted: Failed to expand settings: %s", error)); + return 0; + } if (set->verbose_proctitle) { verbose_proctitle = TRUE; diff --git a/src/imap/imap-client.c b/src/imap/imap-client.c index c6000b3518..e2bf126644 100644 --- a/src/imap/imap-client.c +++ b/src/imap/imap-client.c @@ -251,9 +251,13 @@ const char *client_stats(struct client *client) { '\0', NULL, NULL } }; string_t *str; + const char *error; str = t_str_new(128); - var_expand(str, client->set->imap_logout_format, tab); + if (var_expand(str, client->set->imap_logout_format, tab, &error) <= 0) { + i_error("Failed to expand imap_logout_format=%s: %s", + client->set->imap_logout_format, error); + } return str_c(str); } diff --git a/src/imap/main.c b/src/imap/main.c index fe18e4a99c..8384a419e6 100644 --- a/src/imap/main.c +++ b/src/imap/main.c @@ -251,10 +251,17 @@ int client_create_from_input(const struct mail_storage_service_input *input, verbose_proctitle = TRUE; lda_set = mail_storage_service_user_get_set(user)[2]; - settings_var_expand(&imap_setting_parser_info, imap_set, - mail_user->pool, mail_user_var_expand_table(mail_user)); - settings_var_expand(&lda_setting_parser_info, lda_set, - mail_user->pool, mail_user_var_expand_table(mail_user)); + if (settings_var_expand(&imap_setting_parser_info, imap_set, + mail_user->pool, mail_user_var_expand_table(mail_user), + &errstr) <= 0 || + settings_var_expand(&lda_setting_parser_info, lda_set, + mail_user->pool, mail_user_var_expand_table(mail_user), + &errstr) <= 0) { + *error_r = t_strdup_printf("Failed to expand settings: %s", errstr); + mail_user_unref(&mail_user); + mail_storage_service_user_free(&user); + return -1; + } client = client_create(fd_in, fd_out, input->session_id, mail_user, user, imap_set, lda_set); diff --git a/src/lda/main.c b/src/lda/main.c index df6dddc6e2..964850f82a 100644 --- a/src/lda/main.c +++ b/src/lda/main.c @@ -425,9 +425,11 @@ int main(int argc, char *argv[]) lib_signals_ignore(SIGXFSZ, TRUE); #endif lda_set = mail_storage_service_user_get_set(service_user)[1]; - settings_var_expand(&lda_setting_parser_info, lda_set, - ctx.dest_user->pool, - mail_user_var_expand_table(ctx.dest_user)); + if (settings_var_expand(&lda_setting_parser_info, lda_set, + ctx.dest_user->pool, + mail_user_var_expand_table(ctx.dest_user), + &errstr) <= 0) + i_fatal("Failed to expand settings: %s", errstr); ctx.set = lda_set; if (ctx.dest_user->mail_debug && *user_source != '\0') { diff --git a/src/lib-lda/mail-deliver.c b/src/lib-lda/mail-deliver.c index 4f753de7df..a57e21d4ca 100644 --- a/src/lib-lda/mail-deliver.c +++ b/src/lib-lda/mail-deliver.c @@ -146,7 +146,7 @@ void mail_deliver_log(struct mail_deliver_context *ctx, const char *fmt, ...) { va_list args; string_t *str; - const char *msg; + const char *msg, *error; if (*ctx->set->deliver_log_format == '\0') return; @@ -161,7 +161,11 @@ void mail_deliver_log(struct mail_deliver_context *ctx, const char *fmt, ...) /* update %$ */ ctx->var_expand_table[0].value = msg; mail_deliver_log_var_expand_table_update_times(ctx, ctx->var_expand_table); - var_expand(str, ctx->set->deliver_log_format, ctx->var_expand_table); + if (var_expand(str, ctx->set->deliver_log_format, + ctx->var_expand_table, &error) <= 0) { + i_error("Failed to expand deliver_log_format=%s: %s", + ctx->set->deliver_log_format, error); + } ctx->var_expand_table[0].value = ""; ctx->var_expand_table[VAR_EXPAND_DELIVERY_TIME_IDX].value = ""; diff --git a/src/lib-lda/mail-send.c b/src/lib-lda/mail-send.c index eb2b94720e..405e64ff01 100644 --- a/src/lib-lda/mail-send.c +++ b/src/lib-lda/mail-send.c @@ -98,8 +98,11 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, ctx->dsn ? "delivery-status" : "disposition-notification", boundary); str_append(str, "Subject: "); - var_expand(str, ctx->set->rejection_subject, - get_var_expand_table(mail, reason, recipient)); + if (var_expand(str, ctx->set->rejection_subject, + get_var_expand_table(mail, reason, recipient), &error) <= 0) { + i_error("Failed to expand rejection_subject=%s: %s", + ctx->set->rejection_subject, error); + } str_append(str, "\r\n"); str_append(str, "Auto-Submitted: auto-replied (rejected)\r\n"); @@ -112,8 +115,11 @@ int mail_send_rejection(struct mail_deliver_context *ctx, const char *recipient, str_append(str, "Content-Disposition: inline\r\n"); str_append(str, "Content-Transfer-Encoding: 8bit\r\n\r\n"); - var_expand(str, ctx->set->rejection_reason, - get_var_expand_table(mail, reason, recipient)); + if (var_expand(str, ctx->set->rejection_reason, + get_var_expand_table(mail, reason, recipient), &error) <= 0) { + i_error("Failed to expand rejection_reason=%s: %s", + ctx->set->rejection_reason, error); + } str_append(str, "\r\n"); if (ctx->dsn) { diff --git a/src/lib-mail/mail-user-hash.c b/src/lib-mail/mail-user-hash.c index 1c7bc85075..05ffa0d789 100644 --- a/src/lib-mail/mail-user-hash.c +++ b/src/lib-mail/mail-user-hash.c @@ -6,10 +6,13 @@ #include "var-expand.h" #include "mail-user-hash.h" -unsigned int mail_user_hash(const char *username, const char *format) +bool mail_user_hash(const char *username, const char *format, + unsigned int *hash_r, const char **error_r) { unsigned char md5[MD5_RESULTLEN]; unsigned int i, hash = 0; + char *error_dup = NULL; + int ret = 1; if (strcmp(format, "%u") == 0) { /* fast path */ @@ -28,12 +31,18 @@ unsigned int mail_user_hash(const char *username, const char *format) { '\0', NULL, NULL } }; string_t *str = t_str_new(128); + const char *error; - var_expand(str, format, tab); + ret = var_expand(str, format, tab, &error); + i_assert(ret >= 0); + if (ret == 0) + error_dup = i_strdup(error); md5_get_digest(str_data(str), str_len(str), md5); } T_END; for (i = 0; i < sizeof(hash); i++) hash = (hash << CHAR_BIT) | md5[i]; - return hash; + *hash_r = hash; + *error_r = t_strdup(error_dup); + i_free(error_dup); + return ret > 0; } - diff --git a/src/lib-mail/mail-user-hash.h b/src/lib-mail/mail-user-hash.h index 3287dfcd05..5362389a69 100644 --- a/src/lib-mail/mail-user-hash.h +++ b/src/lib-mail/mail-user-hash.h @@ -1,8 +1,9 @@ #ifndef MAIL_USER_HASH #define MAIL_USER_HASH -/* Return a hash for username, based on given format. The format can use - %n, %d and %u variables. */ -unsigned int mail_user_hash(const char *username, const char *format); +/* Get a hash for username, based on given format. The format can use + %n, %d and %u variables. Returns TRUE if ok, FALSE if format is invalid. */ +bool mail_user_hash(const char *username, const char *format, + unsigned int *hash_r, const char **error_r); #endif diff --git a/src/lib-settings/settings-parser.c b/src/lib-settings/settings-parser.c index cb3afd0211..736d2f09ec 100644 --- a/src/lib-settings/settings-parser.c +++ b/src/lib-settings/settings-parser.c @@ -1269,16 +1269,19 @@ void settings_parse_set_keys_expandeded(struct setting_parser_context *ctx, settings_parse_set_key_expandeded(ctx, pool, *keys); } -static void ATTR_NULL(3, 4, 5) +static int ATTR_NULL(3, 4, 5) settings_var_expand_info(const struct setting_parser_info *info, void *set, pool_t pool, const struct var_expand_table *table, const struct var_expand_func_table *func_table, - void *func_context, string_t *str) + void *func_context, string_t *str, + const char **error_r) { const struct setting_define *def; void *value, *const *children; + const char *error; unsigned int i, count; + int ret, final_ret = 1; for (def = info->defines; def->key != NULL; def++) { value = PTR_OFFSET(set, def->offset); @@ -1307,8 +1310,14 @@ settings_var_expand_info(const struct setting_parser_info *info, void *set, *val += 1; } else if (**val == SETTING_STRVAR_UNEXPANDED[0]) { str_truncate(str, 0); - var_expand_with_funcs(str, *val + 1, table, - func_table, func_context); + ret = var_expand_with_funcs(str, *val + 1, table, + func_table, func_context, + &error); + if (final_ret > ret) { + final_ret = ret; + *error_r = t_strdup_printf( + "%s: %s", def->key, error); + } *val = p_strdup(pool, str_c(str)); } else { i_assert(**val == SETTING_STRVAR_EXPANDED[0]); @@ -1325,46 +1334,64 @@ settings_var_expand_info(const struct setting_parser_info *info, void *set, children = array_get(val, &count); for (i = 0; i < count; i++) { - settings_var_expand_info(def->list_info, + ret = settings_var_expand_info(def->list_info, children[i], pool, table, func_table, - func_context, str); + func_context, str, &error); + if (final_ret > ret) { + final_ret = ret; + *error_r = error; + } } break; } } } + return final_ret; } -void settings_var_expand(const struct setting_parser_info *info, - void *set, pool_t pool, - const struct var_expand_table *table) +int settings_var_expand(const struct setting_parser_info *info, + void *set, pool_t pool, + const struct var_expand_table *table, + const char **error_r) { - settings_var_expand_with_funcs(info, set, pool, table, NULL, NULL); + return settings_var_expand_with_funcs(info, set, pool, table, + NULL, NULL, error_r); } -void settings_var_expand_with_funcs(const struct setting_parser_info *info, - void *set, pool_t pool, - const struct var_expand_table *table, - const struct var_expand_func_table *func_table, - void *func_context) +int settings_var_expand_with_funcs(const struct setting_parser_info *info, + void *set, pool_t pool, + const struct var_expand_table *table, + const struct var_expand_func_table *func_table, + void *func_context, const char **error_r) { - string_t *str; + char *error_dup = NULL; + int ret; T_BEGIN { - str = t_str_new(256); - settings_var_expand_info(info, set, pool, table, - func_table, func_context, str); + const char *error; + string_t *str = t_str_new(256); + + ret = settings_var_expand_info(info, set, pool, table, + func_table, func_context, str, + &error); + if (ret <= 0) + error_dup = i_strdup(error); } T_END; + *error_r = t_strdup(error_dup); + i_free(error_dup); + return ret; } void settings_parse_var_skip(struct setting_parser_context *ctx) { unsigned int i; + const char *error; for (i = 0; i < ctx->root_count; i++) { - settings_var_expand_info(ctx->roots[i].info, - ctx->roots[i].set_struct, - NULL, NULL, NULL, NULL, NULL); + (void)settings_var_expand_info(ctx->roots[i].info, + ctx->roots[i].set_struct, + NULL, NULL, NULL, NULL, NULL, + &error); } } diff --git a/src/lib-settings/settings-parser.h b/src/lib-settings/settings-parser.h index 28b3fac78d..ed0345e821 100644 --- a/src/lib-settings/settings-parser.h +++ b/src/lib-settings/settings-parser.h @@ -185,15 +185,17 @@ void settings_parse_set_keys_expandeded(struct setting_parser_context *ctx, actually knowing what the variables are. */ void settings_parse_var_skip(struct setting_parser_context *ctx); /* Expand all unexpanded variables using the given table. Update the string - pointers so that they can be used without skipping over the '1'. */ -void settings_var_expand(const struct setting_parser_info *info, - void *set, pool_t pool, - const struct var_expand_table *table); -void settings_var_expand_with_funcs(const struct setting_parser_info *info, - void *set, pool_t pool, - const struct var_expand_table *table, - const struct var_expand_func_table *func_table, - void *func_context); + pointers so that they can be used without skipping over the '1'. + Returns the same as var_expand(). */ +int settings_var_expand(const struct setting_parser_info *info, + void *set, pool_t pool, + const struct var_expand_table *table, + const char **error_r); +int settings_var_expand_with_funcs(const struct setting_parser_info *info, + void *set, pool_t pool, + const struct var_expand_table *table, + const struct var_expand_func_table *func_table, + void *func_context, const char **error_r); /* Go through all the settings and return the first one that has an unexpanded setting containing the given %key. */ bool settings_vars_have_key(const struct setting_parser_info *info, void *set, diff --git a/src/lib-sql/driver-cassandra.c b/src/lib-sql/driver-cassandra.c index c5361a913d..9cbc4ed437 100644 --- a/src/lib-sql/driver-cassandra.c +++ b/src/lib-sql/driver-cassandra.c @@ -529,9 +529,14 @@ static void driver_cassandra_metrics_write(struct cassandra_db *db) }; string_t *path = t_str_new(64); string_t *data; + const char *error; int fd; - var_expand(path, db->metrics_path, tab); + if (var_expand(path, db->metrics_path, tab, &error) <= 0) { + i_error("cassandra: Failed to expand metrics_path=%s: %s", + db->metrics_path, error); + return; + } fd = open(str_c(path), O_WRONLY | O_CREAT | O_TRUNC | O_NONBLOCK, 0600); if (fd == -1) { diff --git a/src/lib-storage/index/shared/shared-storage.c b/src/lib-storage/index/shared/shared-storage.c index 06965538ad..b742c54bfb 100644 --- a/src/lib-storage/index/shared/shared-storage.c +++ b/src/lib-storage/index/shared/shared-storage.c @@ -236,7 +236,12 @@ int shared_storage_get_namespace(struct mail_namespace **_ns, prefix = t_str_new(128); str_append(prefix, ns->prefix); - var_expand(prefix, storage->ns_prefix_pattern, tab); + if (var_expand(prefix, storage->ns_prefix_pattern, tab, &error) <= 0) { + mailbox_list_set_critical(list, + "Failed to expand namespace prefix '%s': %s", + storage->ns_prefix_pattern, error); + return -1; + } *_ns = mail_namespace_find_prefix(user->namespaces, str_c(prefix)); if (*_ns != NULL) { @@ -273,6 +278,15 @@ int shared_storage_get_namespace(struct mail_namespace **_ns, } } + location = t_str_new(256); + if (ret > 0 && + var_expand(location, storage->location, tab, &error) <= 0) { + mailbox_list_set_critical(list, + "Failed to expand namespace location '%s': %s", + storage->location, error); + return -1; + } + /* create the new namespace */ new_ns = i_new(struct mail_namespace, 1); new_ns->refcount = 1; @@ -286,10 +300,7 @@ int shared_storage_get_namespace(struct mail_namespace **_ns, new_ns->mail_set = _storage->set; i_array_init(&new_ns->all_storages, 2); - location = t_str_new(256); - if (ret > 0) - var_expand(location, storage->location, tab); - else { + if (ret <= 0) { get_nonexistent_user_location(storage, userdomain, location); new_ns->flags |= NAMESPACE_FLAG_UNUSABLE; if (ns->user->mail_debug) { diff --git a/src/lib-storage/mail-storage-service.c b/src/lib-storage/mail-storage-service.c index 83dffe9fa0..74b89c3fc8 100644 --- a/src/lib-storage/mail-storage-service.c +++ b/src/lib-storage/mail-storage-service.c @@ -90,12 +90,13 @@ struct mail_storage_service_user { struct module *mail_storage_service_modules = NULL; -static void +static int mail_storage_service_var_expand(struct mail_storage_service_ctx *ctx, string_t *str, const char *format, struct mail_storage_service_user *user, const struct mail_storage_service_input *input, - const struct mail_storage_service_privileges *priv); + const struct mail_storage_service_privileges *priv, + const char **error_r); static bool mail_user_set_get_mail_debug(const struct setting_parser_info *user_info, @@ -447,22 +448,27 @@ mail_storage_service_get_var_expand_table(struct mail_storage_service_ctx *ctx, return get_var_expand_table(ctx->service, NULL, input, &priv); } -static const char * +static bool user_expand_varstr(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_storage_service_privileges *priv, - const char *str) + const char *str, const char **value_r, const char **error_r) { - string_t *ret; + string_t *value; + int ret; - if (*str == SETTING_STRVAR_EXPANDED[0]) - return str + 1; + if (*str == SETTING_STRVAR_EXPANDED[0]) { + *value_r = str + 1; + return 1; + } i_assert(*str == SETTING_STRVAR_UNEXPANDED[0]); - ret = t_str_new(256); - mail_storage_service_var_expand(ctx, ret, str + 1, user, &user->input, priv); - return str_c(ret); + value = t_str_new(256); + ret = mail_storage_service_var_expand(ctx, value, str + 1, user, + &user->input, priv, error_r); + *value_r = str_c(value); + return ret > 0; } static int @@ -474,6 +480,7 @@ service_parse_privileges(struct mail_storage_service_ctx *ctx, const struct mail_user_settings *set = user->user_set; uid_t uid = (uid_t)-1; gid_t gid = (gid_t)-1; + const char *error; memset(priv_r, 0, sizeof(*priv_r)); if (*set->mail_uid != '\0') { @@ -516,10 +523,20 @@ service_parse_privileges(struct mail_storage_service_ctx *ctx, /* variable strings are expanded in mail_user_init(), but we need the home and chroot sooner so do them separately here. */ - priv_r->home = user_expand_varstr(ctx, user, priv_r, - user->user_set->mail_home); - priv_r->chroot = user_expand_varstr(ctx, user, priv_r, - user->user_set->mail_chroot); + if (!user_expand_varstr(ctx, user, priv_r, user->user_set->mail_home, + &priv_r->home, &error) <= 0) { + *error_r = t_strdup_printf( + "Failed to expand mail_home '%s': %s", + user->user_set->mail_home, error); + return -1; + } + if (!user_expand_varstr(ctx, user, priv_r, user->user_set->mail_chroot, + &priv_r->chroot, &error)) { + *error_r = t_strdup_printf( + "Failed to expand mail_chroot '%s': %s", + user->user_set->mail_chroot, error); + return -1; + } return 0; } @@ -785,20 +802,21 @@ mail_storage_service_input_var_userdb(const char *data, void *context) user == NULL ? NULL : user->input.userdb_fields); } -static void +static int mail_storage_service_var_expand(struct mail_storage_service_ctx *ctx, string_t *str, const char *format, struct mail_storage_service_user *user, const struct mail_storage_service_input *input, - const struct mail_storage_service_privileges *priv) + const struct mail_storage_service_privileges *priv, + const char **error_r) { static const struct var_expand_func_table func_table[] = { { "userdb", mail_storage_service_input_var_userdb }, { NULL, NULL } }; - var_expand_with_funcs(str, format, + return var_expand_with_funcs(str, format, get_var_expand_table(ctx->service, user, input, priv), - func_table, user); + func_table, user, error_r); } static void @@ -806,14 +824,16 @@ mail_storage_service_init_log(struct mail_storage_service_ctx *ctx, struct mail_storage_service_user *user, struct mail_storage_service_privileges *priv) { + const char *error; + ctx->log_initialized = TRUE; T_BEGIN { string_t *str; str = t_str_new(256); - mail_storage_service_var_expand(ctx, str, + (void)mail_storage_service_var_expand(ctx, str, user->user_set->mail_log_prefix, - user, &user->input, priv); + user, &user->input, priv, &error); user->log_prefix = p_strdup(user->pool, str_c(str)); } T_END; @@ -1106,10 +1126,11 @@ mail_storage_service_set_log_prefix(struct mail_storage_service_ctx *ctx, const struct mail_storage_service_privileges *priv) { string_t *str; + const char *error; str = t_str_new(256); - mail_storage_service_var_expand(ctx, str, user_set->mail_log_prefix, - user, input, priv); + (void)mail_storage_service_var_expand(ctx, str, user_set->mail_log_prefix, + user, input, priv, &error); i_set_failure_prefix("%s", str_c(str)); } diff --git a/src/lib-storage/mail-user.c b/src/lib-storage/mail-user.c index 771078b17c..5083b1bce2 100644 --- a/src/lib-storage/mail-user.c +++ b/src/lib-storage/mail-user.c @@ -80,7 +80,7 @@ struct mail_user *mail_user_alloc(const char *username, static void mail_user_expand_plugins_envs(struct mail_user *user) { - const char **envs, *home; + const char **envs, *home, *error; string_t *str; unsigned int i, count; @@ -101,9 +101,15 @@ mail_user_expand_plugins_envs(struct mail_user *user) return; } str_truncate(str, 0); - var_expand_with_funcs(str, envs[i+1], - mail_user_var_expand_table(user), - mail_user_var_expand_func_table, user); + if (var_expand_with_funcs(str, envs[i+1], + mail_user_var_expand_table(user), + mail_user_var_expand_func_table, user, + &error) <= 0) { + user->error = p_strdup_printf(user->pool, + "Failed to expand plugin setting %s = '%s': %s", + envs[i], envs[i+1], error); + return; + } envs[i+1] = p_strdup(user->pool, str_c(str)); } } @@ -111,7 +117,7 @@ mail_user_expand_plugins_envs(struct mail_user *user) int mail_user_init(struct mail_user *user, const char **error_r) { const struct mail_storage_settings *mail_set; - const char *home, *key, *value; + const char *home, *key, *value, *error; bool need_home_dir; need_home_dir = user->_home == NULL && @@ -124,9 +130,13 @@ int mail_user_init(struct mail_user *user, const char **error_r) } /* expand settings after we can expand %h */ - settings_var_expand_with_funcs(user->set_info, user->set, - user->pool, mail_user_var_expand_table(user), - mail_user_var_expand_func_table, user); + if (settings_var_expand_with_funcs(user->set_info, user->set, + user->pool, mail_user_var_expand_table(user), + mail_user_var_expand_func_table, user, + &error) <= 0) { + user->error = p_strdup_printf(user->pool, + "Failed to expand settings: %s", error); + } user->settings_expanded = TRUE; mail_user_expand_plugins_envs(user); @@ -352,26 +362,31 @@ static int mail_user_userdb_lookup_home(struct mail_user *user) return ret; } -static void mail_user_get_mail_home(struct mail_user *user) +static bool mail_user_get_mail_home(struct mail_user *user) { - const char *home = user->set->mail_home; + const char *error, *home = user->set->mail_home; string_t *str; if (user->settings_expanded) { user->_home = home[0] != '\0' ? home : NULL; - return; + return TRUE; } /* we're still initializing user. need to do the expansion ourself. */ i_assert(home[0] == SETTING_STRVAR_UNEXPANDED[0]); home++; if (home[0] == '\0') - return; + return TRUE; str = t_str_new(128); - var_expand_with_funcs(str, home, - mail_user_var_expand_table(user), - mail_user_var_expand_func_table, user); + if (var_expand_with_funcs(str, home, + mail_user_var_expand_table(user), + mail_user_var_expand_func_table, user, + &error) <= 0) { + i_error("Failed to expand mail_home=%s: %s", home, error); + return FALSE; + } user->_home = p_strdup(user->pool, str_c(str)); + return TRUE; } int mail_user_get_home(struct mail_user *user, const char **home_r) @@ -385,7 +400,8 @@ int mail_user_get_home(struct mail_user *user, const char **home_r) if (mail_user_auth_master_conn == NULL) { /* no userdb connection. we can only use mail_home setting. */ - mail_user_get_mail_home(user); + if (!mail_user_get_mail_home(user)) + return -1; } else if ((ret = mail_user_userdb_lookup_home(user)) < 0) { /* userdb lookup failed */ return -1; @@ -395,7 +411,8 @@ int mail_user_get_home(struct mail_user *user, const char **home_r) } else if (user->_home == NULL) { /* no home returned by userdb lookup, fallback to mail_home setting. */ - mail_user_get_mail_home(user); + if (!mail_user_get_mail_home(user)) + return -1; } user->home_looked_up = TRUE; diff --git a/src/lib/test-var-expand.c b/src/lib/test-var-expand.c index 080a0739a4..8b23f7ce3d 100644 --- a/src/lib/test-var-expand.c +++ b/src/lib/test-var-expand.c @@ -9,6 +9,7 @@ struct var_expand_test { const char *in; const char *out; + int ret; }; struct var_get_key_range_test { @@ -19,25 +20,26 @@ struct var_get_key_range_test { static void test_var_expand_ranges(void) { static struct var_expand_test tests[] = { - { "%v", "value1234" }, - { "%3v", "val" }, - { "%3.2v", "ue" }, - { "%3.-2v", "ue12" }, - { "%-3.2v", "23" }, - { "%0.-1v", "value123" }, - { "%-4.-1v", "123" } + { "%v", "value1234", 1 }, + { "%3v", "val", 1 }, + { "%3.2v", "ue", 1 }, + { "%3.-2v", "ue12", 1 }, + { "%-3.2v", "23", 1 }, + { "%0.-1v", "value123", 1 }, + { "%-4.-1v", "123", 1 } }; static struct var_expand_table table[] = { { 'v', "value1234", NULL }, { '\0', NULL, NULL } }; string_t *str = t_str_new(128); + const char *error; unsigned int i; test_begin("var_expand - ranges"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); - var_expand(str, tests[i].in, table); + test_assert(var_expand(str, tests[i].in, table, &error) == tests[i].ret); test_assert(strcmp(tests[i].out, str_c(str)) == 0); } test_end(); @@ -46,16 +48,16 @@ static void test_var_expand_ranges(void) static void test_var_expand_builtin(void) { static struct var_expand_test tests[] = { - { "%{hostname}", NULL }, - { "%{pid}", NULL }, - { "a%{env:FOO}b", "abaRb" }, - { "%50Hv", "1f" }, - { "%50Hw", "2e" }, - { "%50Nv", "25" }, - { "%50Nw", "e" }, - - { "%{nonexistent}", "UNSUPPORTED_VARIABLE_nonexistent" }, - { "%{nonexistent:default}", "UNSUPPORTED_VARIABLE_nonexistent" }, + { "%{hostname}", NULL, 1 }, + { "%{pid}", NULL, 1 }, + { "a%{env:FOO}b", "abaRb", 1 }, + { "%50Hv", "1f", 1 }, + { "%50Hw", "2e", 1 }, + { "%50Nv", "25", 1 }, + { "%50Nw", "e", 1 }, + + { "%{nonexistent}", "UNSUPPORTED_VARIABLE_nonexistent", 0 }, + { "%{nonexistent:default}", "UNSUPPORTED_VARIABLE_nonexistent", 0 }, }; static struct var_expand_table table[] = { { 'v', "value", NULL }, @@ -63,6 +65,7 @@ static void test_var_expand_builtin(void) { '\0', NULL, NULL } }; string_t *str = t_str_new(128); + const char *error; unsigned int i; tests[0].out = my_hostname; @@ -72,7 +75,7 @@ static void test_var_expand_builtin(void) test_begin("var_expand - builtin"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); - var_expand(str, tests[i].in, table); + test_assert_idx(var_expand(str, tests[i].in, table, &error) == tests[i].ret, i); test_assert_idx(strcmp(tests[i].out, str_c(str)) == 0, i); } test_end(); @@ -124,10 +127,10 @@ static const char *test_var_expand_func3(const char *data ATTR_UNUSED, static void test_var_expand_with_funcs(void) { static struct var_expand_test tests[] = { - { "%{func1}", "<>" }, - { "%{func1:foo}", "" }, - { "%{func2}", "" }, - { "%{func3}", "" } + { "%{func1}", "<>", 1 }, + { "%{func1:foo}", "", 1 }, + { "%{func2}", "", 1 }, + { "%{func3}", "", 1 } }; static struct var_expand_table table[] = { { '\0', NULL, NULL } @@ -139,13 +142,14 @@ static void test_var_expand_with_funcs(void) { NULL, NULL } }; string_t *str = t_str_new(128); + const char *error; unsigned int i; int ctx = 0xabcdef; test_begin("var_expand_with_funcs"); for (i = 0; i < N_ELEMENTS(tests); i++) { str_truncate(str, 0); - var_expand_with_funcs(str, tests[i].in, table, func_table, &ctx); + test_assert_idx(var_expand_with_funcs(str, tests[i].in, table, func_table, &ctx, &error) == tests[i].ret, i); test_assert_idx(strcmp(tests[i].out, str_c(str)) == 0, i); } test_end(); diff --git a/src/lib/var-expand.c b/src/lib/var-expand.c index 5e61ace7a9..2483cbf047 100644 --- a/src/lib/var-expand.c +++ b/src/lib/var-expand.c @@ -166,60 +166,75 @@ static const struct var_expand_modifier modifiers[] = { { '\0', NULL } }; -static const char * -var_expand_short(const struct var_expand_table *table, char key) +static int +var_expand_short(const struct var_expand_table *table, char key, + const char **var_r, const char **error_r) { const struct var_expand_table *t; if (table != NULL) { for (t = table; !TABLE_LAST(t); t++) { - if (t->key == key) - return t->value != NULL ? t->value : ""; + if (t->key == key) { + *var_r = t->value != NULL ? t->value : ""; + return 1; + } } } /* not found */ - if (key == '%') - return "%"; - return NULL; + if (key == '%') { + *var_r = "%"; + return 1; + } + if (*error_r == NULL) + *error_r = t_strdup_printf("Unknown variable '%%%c'", key); + return 0; } -static const char * +static int var_expand_func(const struct var_expand_func_table *func_table, - const char *key, const char *data, void *context) + const char *key, const char *data, void *context, + const char **var_r, const char **error_r) { const char *value; if (strcmp(key, "env") == 0) { value = getenv(data); - return value != NULL ? value : ""; + *var_r = value != NULL ? value : ""; + return 1; } - if (func_table == NULL) - return NULL; - - for (; func_table->key != NULL; func_table++) { - if (strcmp(func_table->key, key) == 0) { - value = func_table->func(data, context); - return value != NULL ? value : ""; + if (func_table != NULL) { + for (; func_table->key != NULL; func_table++) { + if (strcmp(func_table->key, key) == 0) { + value = func_table->func(data, context); + *var_r = value != NULL ? value : ""; + return 1; + } } } - return NULL; + if (*error_r == NULL) + *error_r = t_strdup_printf("Unknown variable '%%%s'", key); + *var_r = t_strdup_printf("UNSUPPORTED_VARIABLE_%s", key); + return 0; } -static const char * +static int var_expand_long(const struct var_expand_table *table, const struct var_expand_func_table *func_table, - const void *key_start, unsigned int key_len, void *context) + const void *key_start, unsigned int key_len, void *context, + const char **var_r, const char **error_r) { const struct var_expand_table *t; const char *key, *value = NULL; + int ret = 1; if (table != NULL) { for (t = table; !TABLE_LAST(t); t++) { if (t->long_key != NULL && strncmp(t->long_key, key_start, key_len) == 0 && t->long_key[key_len] == '\0') { - return t->value != NULL ? t->value : ""; + *var_r = t->value != NULL ? t->value : ""; + return 1; } } } @@ -248,17 +263,16 @@ var_expand_long(const struct var_expand_table *table, key = t_strdup_until(key, data++); else data = ""; - value = var_expand_func(func_table, key, data, context); + ret = var_expand_func(func_table, key, data, context, &value, error_r); } - if (value == NULL) - return t_strdup_printf("UNSUPPORTED_VARIABLE_%s", key); - return value; + *var_r = value; + return ret; } -void var_expand_with_funcs(string_t *dest, const char *str, - const struct var_expand_table *table, - const struct var_expand_func_table *func_table, - void *context) +int var_expand_with_funcs(string_t *dest, const char *str, + const struct var_expand_table *table, + const struct var_expand_func_table *func_table, + void *context, const char **error_r) { const struct var_expand_modifier *m; const char *var; @@ -267,6 +281,9 @@ void var_expand_with_funcs(string_t *dest, const char *str, (const char *, struct var_expand_context *); const char *end; unsigned int i, len, modifier_count; + int ret, final_ret = 1; + + *error_r = NULL; memset(&ctx, 0, sizeof(ctx)); for (; *str != '\0'; str++) { @@ -342,13 +359,16 @@ void var_expand_with_funcs(string_t *dest, const char *str, if (*str == '{' && (end = strchr(str, '}')) != NULL) { /* %{long_key} */ len = end - (str + 1); - var = var_expand_long(table, func_table, - str+1, len, context); + ret = var_expand_long(table, func_table, + str+1, len, context, + &var, error_r); i_assert(var != NULL); str = end; } else { - var = var_expand_short(table, *str); + ret = var_expand_short(table, *str, &var, error_r); } + if (final_ret > ret) + final_ret = ret; if (var != NULL) { for (i = 0; i < modifier_count; i++) @@ -385,12 +405,13 @@ void var_expand_with_funcs(string_t *dest, const char *str, } } } + return final_ret; } -void var_expand(string_t *dest, const char *str, - const struct var_expand_table *table) +int var_expand(string_t *dest, const char *str, + const struct var_expand_table *table, const char **error_r) { - var_expand_with_funcs(dest, str, table, NULL, NULL); + return var_expand_with_funcs(dest, str, table, NULL, NULL, error_r); } static bool diff --git a/src/lib/var-expand.h b/src/lib/var-expand.h index aea8d77b33..e93884fd80 100644 --- a/src/lib/var-expand.h +++ b/src/lib/var-expand.h @@ -14,15 +14,18 @@ struct var_expand_func_table { }; /* Expand % variables in src and append the string in dest. - table must end with key = 0. */ -void var_expand(string_t *dest, const char *str, - const struct var_expand_table *table); + table must end with key = 0. Returns 1 on success, 0 if the format string + contained invalid/unknown %variables. Even in case of errors the dest string + is still written as fully as possible. */ +int var_expand(string_t *dest, const char *str, + const struct var_expand_table *table, + const char **error_r); /* Like var_expand(), but support also callback functions for variable expansion. */ -void var_expand_with_funcs(string_t *dest, const char *str, - const struct var_expand_table *table, - const struct var_expand_func_table *func_table, - void *func_context) ATTR_NULL(3, 4, 5); +int var_expand_with_funcs(string_t *dest, const char *str, + const struct var_expand_table *table, + const struct var_expand_func_table *func_table, + void *func_context, const char **error_r) ATTR_NULL(3, 4, 5); /* Returns the actual key character for given string, ie. skip any modifiers that are before it. The string should be the data after the '%' character. diff --git a/src/lmtp/client.c b/src/lmtp/client.c index e922958be2..6dbdad7885 100644 --- a/src/lmtp/client.c +++ b/src/lmtp/client.c @@ -192,8 +192,11 @@ static void client_read_settings(struct client *client) i_fatal("%s", error); lmtp_settings_dup(set_parser, client->pool, &lmtp_set, &lda_set); - settings_var_expand(&lmtp_setting_parser_info, lmtp_set, client->pool, - mail_storage_service_get_var_expand_table(storage_service, &input)); + const struct var_expand_table *tab = + mail_storage_service_get_var_expand_table(storage_service, &input); + if (settings_var_expand(&lmtp_setting_parser_info, lmtp_set, + client->pool, tab, &error) <= 0) + i_fatal("Failed to expand settings: %s", error); client->service_set = master_service_settings_get(master_service); client->lmtp_set = lmtp_set; client->unexpanded_lda_set = lda_set; diff --git a/src/lmtp/commands.c b/src/lmtp/commands.c index 965390d167..fd1a3b509e 100644 --- a/src/lmtp/commands.c +++ b/src/lmtp/commands.c @@ -851,17 +851,29 @@ client_deliver(struct client *client, const struct mail_recipient *rcpt, rcpt->address); return -1; } - str = t_str_new(256); - var_expand_with_funcs(str, client->state.dest_user->set->mail_log_prefix, - mail_user_var_expand_table(client->state.dest_user), - mail_user_var_expand_func_table, - client->state.dest_user); - i_set_failure_prefix("%s", str_c(str)); sets = mail_storage_service_user_get_set(rcpt->service_user); lda_set = sets[1]; - settings_var_expand(&lda_setting_parser_info, lda_set, client->pool, - mail_user_var_expand_table(client->state.dest_user)); + if (settings_var_expand(&lda_setting_parser_info, lda_set, client->pool, + mail_user_var_expand_table(client->state.dest_user), &error) <= 0) { + i_error("Failed to expand settings: %s", error); + client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, + rcpt->address); + return -1; + } + + str = t_str_new(256); + if (var_expand_with_funcs(str, client->state.dest_user->set->mail_log_prefix, + mail_user_var_expand_table(client->state.dest_user), + mail_user_var_expand_func_table, + client->state.dest_user, &error) <= 0) { + i_error("Failed to expand mail_log_prefix=%s: %s", + client->state.dest_user->set->mail_log_prefix, error); + client_send_line(client, ERRSTR_TEMP_MAILBOX_FAIL, + rcpt->address); + return -1; + } + i_set_failure_prefix("%s", str_c(str)); memset(&dctx, 0, sizeof(dctx)); dctx.session = session; diff --git a/src/login-common/client-common.c b/src/login-common/client-common.c index 0ad59f5676..44377998ae 100644 --- a/src/login-common/client-common.c +++ b/src/login-common/client-common.c @@ -627,8 +627,10 @@ client_get_log_str(struct client *client, const char *msg) { "passdb", client_var_expand_func_passdb }, { NULL, NULL } }; + static bool expand_error_logged = FALSE; const struct var_expand_table *var_expand_table; char *const *e; + const char *error; string_t *str, *str2; unsigned int pos; @@ -638,13 +640,22 @@ client_get_log_str(struct client *client, const char *msg) str2 = t_str_new(128); for (e = client->set->log_format_elements_split; *e != NULL; e++) { pos = str_len(str); - var_expand_with_funcs(str, *e, var_expand_table, - func_table, client); + if (var_expand_with_funcs(str, *e, var_expand_table, + func_table, client, &error) <= 0 && + !expand_error_logged) { + i_error("Failed to expand log_format_elements=%s: %s", + *e, error); + expand_error_logged = TRUE; + } if (have_username_key(*e)) { /* username is added even if it's empty */ } else { str_truncate(str2, 0); - var_expand(str2, *e, login_var_expand_empty_tab); + if (var_expand(str2, *e, login_var_expand_empty_tab, + &error) <= 0) { + /* we just logged this error above. no need + to do it again. */ + } if (strcmp(str_c(str)+pos, str_c(str2)) == 0) { /* empty %variables, don't add */ str_truncate(str, pos); @@ -666,7 +677,11 @@ client_get_log_str(struct client *client, const char *msg) }; str_truncate(str, 0); - var_expand(str, client->set->login_log_format, tab); + if (var_expand(str, client->set->login_log_format, tab, &error) <= 0) { + i_error("Failed to expand login_log_format=%s: %s", + client->set->login_log_format, error); + expand_error_logged = TRUE; + } return str_c(str); } diff --git a/src/login-common/login-proxy.c b/src/login-common/login-proxy.c index 8cfa86a414..623ec06388 100644 --- a/src/login-common/login-proxy.c +++ b/src/login-common/login-proxy.c @@ -920,10 +920,18 @@ login_proxy_cmd_kick_alt(struct ipc_cmd *cmd, const char *const *args) login_proxy_cmd_kick_full(cmd, args+1, want_kick_alt_username, i); } -static unsigned int director_username_hash(struct client *client) +static bool director_username_hash(struct client *client, unsigned int *hash_r) { - return mail_user_hash(client->virtual_user, - client->set->director_username_hash); + const char *error; + + if (!mail_user_hash(client->virtual_user, + client->set->director_username_hash, + hash_r, &error)) { + i_error("Failed to expand director_username_hash=%s: %s", + client->set->director_username_hash, error); + return FALSE; + } + return TRUE; } static void @@ -931,7 +939,7 @@ login_proxy_cmd_kick_director_hash(struct ipc_cmd *cmd, const char *const *args) { struct login_proxy *proxy, *next; struct ip_addr except_ip; - unsigned int hash, count = 0; + unsigned int hash, proxy_hash, count = 0; if (args[0] == NULL || str_to_uint(args[0], &hash) < 0) { ipc_cmd_fail(&cmd, "Invalid parameters"); @@ -949,7 +957,8 @@ login_proxy_cmd_kick_director_hash(struct ipc_cmd *cmd, const char *const *args) for (proxy = login_proxies; proxy != NULL; proxy = next) { next = proxy->next; - if (director_username_hash(proxy->client) == hash && + if (director_username_hash(proxy->client, &proxy_hash) && + proxy_hash == hash && !net_ip_compare(&proxy->ip, &except_ip)) { login_proxy_free_delayed(&proxy, KILLED_BY_DIRECTOR_REASON); count++; @@ -958,7 +967,8 @@ login_proxy_cmd_kick_director_hash(struct ipc_cmd *cmd, const char *const *args) for (proxy = login_proxies_pending; proxy != NULL; proxy = next) { next = proxy->next; - if (director_username_hash(proxy->client) == hash && + if (director_username_hash(proxy->client, &proxy_hash) && + proxy_hash == hash && !net_ip_compare(&proxy->ip, &except_ip)) { client_destroy(proxy->client, "Connection kicked"); count++; diff --git a/src/login-common/login-settings.c b/src/login-common/login-settings.c index 089da72200..e47fa19751 100644 --- a/src/login-common/login-settings.c +++ b/src/login-common/login-settings.c @@ -190,8 +190,9 @@ login_settings_read(pool_t pool, for (i = 0; i < count; i++) sets[i] = login_setting_dup(pool, input.roots[i], cache_sets[i]); - settings_var_expand(&login_setting_parser_info, sets[0], pool, - login_set_var_expand_table(&input)); + if (settings_var_expand(&login_setting_parser_info, sets[0], pool, + login_set_var_expand_table(&input), &error) <= 0) + i_fatal("Failed to expand settings: %s", error); *ssl_set_r = login_setting_dup(pool, &master_service_ssl_setting_parser_info, diff --git a/src/plugins/acl/acl-shared-storage.c b/src/plugins/acl/acl-shared-storage.c index 5790c204d1..54c905a4e8 100644 --- a/src/plugins/acl/acl-shared-storage.c +++ b/src/plugins/acl/acl-shared-storage.c @@ -38,7 +38,7 @@ acl_shared_namespace_add(struct mail_namespace *ns, struct mail_namespace *new_ns = ns; struct mailbox_list_iterate_context *iter; const struct mailbox_info *info; - const char *mailbox; + const char *mailbox, *error; string_t *str; if (strcmp(ns->user->username, userdomain) == 0) { @@ -54,7 +54,11 @@ acl_shared_namespace_add(struct mail_namespace *ns, }; str = t_str_new(128); - var_expand(str, sstorage->ns_prefix_pattern, tab); + if (var_expand(str, sstorage->ns_prefix_pattern, tab, &error) <= 0) { + i_error("Failed to expand namespace prefix %s: %s", + sstorage->ns_prefix_pattern, error); + return; + } mailbox = str_c(str); if (shared_storage_get_namespace(&new_ns, &mailbox) < 0) return; diff --git a/src/plugins/dict-ldap/dict-ldap.c b/src/plugins/dict-ldap/dict-ldap.c index 1ccd473162..699f444db8 100644 --- a/src/plugins/dict-ldap/dict-ldap.c +++ b/src/plugins/dict-ldap/dict-ldap.c @@ -162,12 +162,12 @@ int dict_ldap_connect(struct ldap_dict *dict, const char **error_r) return ldap_client_init(&set, &dict->client, error_r); } -static void +static bool ldap_dict_build_query(struct ldap_dict *dict, const struct dict_ldap_map *map, ARRAY_TYPE(const_string) *values, bool priv, - string_t *query_r) + string_t *query_r, const char **error_r) { - const char *template; + const char *template, *error; ARRAY(struct var_expand_table) exp; struct var_expand_table entry; @@ -192,7 +192,11 @@ ldap_dict_build_query(struct ldap_dict *dict, const struct dict_ldap_map *map, array_append_zero(&exp); - var_expand(query_r, template, array_idx(&exp, 0)); + if (var_expand(query_r, template, array_idx(&exp, 0), &error) <= 0) { + *error_r = t_strdup_printf("Failed to expand %s: %s", template, error); + return FALSE; + } + return TRUE; } static @@ -376,6 +380,8 @@ void ldap_dict_lookup_async(struct dict *dict, const char *key, struct ldap_search_input input; struct ldap_dict *ctx = (struct ldap_dict*)dict; struct dict_ldap_op *op; + const char *error; + pool_t oppool = pool_alloconly_create("ldap dict lookup", 64); string_t *query = str_new(oppool, 64); op = p_new(oppool, struct dict_ldap_op, 1); @@ -398,7 +404,11 @@ void ldap_dict_lookup_async(struct dict *dict, const char *key, memset(&input, 0, sizeof(input)); input.base_dn = map->base_dn; input.scope = map->scope_val; - ldap_dict_build_query(ctx, map, &values, strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE))==0, query); + if (!ldap_dict_build_query(ctx, map, &values, strncmp(key, DICT_PATH_PRIVATE, strlen(DICT_PATH_PRIVATE))==0, query, &error)) { + op->res.error = error; + callback(&(op->res), context); + pool_unref(&oppool); + } input.filter = str_c(query); input.attributes = attributes; input.timeout_secs = ctx->set->timeout; diff --git a/src/pop3/pop3-client.c b/src/pop3/pop3-client.c index d8737fd255..949b973cc4 100644 --- a/src/pop3/pop3-client.c +++ b/src/pop3/pop3-client.c @@ -553,9 +553,13 @@ static const char *client_stats(struct client *client) { '\0', NULL, NULL } }; string_t *str; + const char *error; str = t_str_new(128); - var_expand(str, client->set->pop3_logout_format, tab); + if (var_expand(str, client->set->pop3_logout_format, tab, &error) <= 0) { + i_error("Failed to expand pop3_logout_format=%s: %s", + client->set->pop3_logout_format, error); + } return str_c(str); } diff --git a/src/pop3/pop3-commands.c b/src/pop3/pop3-commands.c index 65fb01eb3c..e2149e7534 100644 --- a/src/pop3/pop3-commands.c +++ b/src/pop3/pop3-commands.c @@ -639,7 +639,14 @@ pop3_get_uid(struct client *client, struct mail *mail, string_t *str, { 'g', guid, "guid" }, { '\0', NULL, NULL } }; - var_expand(str, client->mail_set->pop3_uidl_format, tab); + const char *error; + + if (var_expand(str, client->mail_set->pop3_uidl_format, + tab, &error) <= 0) { + i_error("UIDL: Failed to expand pop3_uidl_format=%s: %s", + client->mail_set->pop3_uidl_format, error); + return -1; + } return 0; }