talons

Fork of Claws Mail https://www.claws-mail
Log | Files | Refs | README | LICENSE

commit 54adfb433b879fa0e9218ed329feb6d99caf0fb1
parent e7374d38e51fa38b5c2f06669be8c3f967c39ba1
Author: Andrej Kacian <ticho@claws-mail.org>
Date:   Sat, 16 Jan 2016 22:13:53 +0100

Rewritten account passwords handling.

Passwords are only decrypted before their actual use, not
while loading from accountrc.
Passwords are stored as "{algorithm}base64encodedciphertext",
encrypted using AES-CBC cipher, with PASSCRYPT_KEY used as
and encryption key.
Optionally, the encryption key, also known as "master password"
can be changed by user.

Diffstat:
Mconfigure.ac | 30++++++++++++++++++++++++++++++
Msrc/Makefile.am | 4++++
Msrc/imap.c | 16+++++++++++++---
Msrc/inc.c | 3++-
Msrc/news.c | 3++-
Asrc/password.c | 551+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/password.h | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/password_gtk.c | 212+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/password_gtk.h | 31+++++++++++++++++++++++++++++++
Msrc/prefs_common.c | 6+++++-
Msrc/prefs_common.h | 6++++++
Msrc/prefs_gtk.c | 54+++++++++++++-----------------------------------------
Msrc/prefs_other.c | 94+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msrc/send_message.c | 5+++--
Msrc/wizard.c | 9+++++----
15 files changed, 1032 insertions(+), 55 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -519,6 +519,35 @@ if test x"$ac_cv_with_config_dir" = x""; then fi AC_DEFINE_UNQUOTED(CFG_RC_DIR, "$ac_cv_with_config_dir", Configuration directory) +AC_ARG_WITH(password-encryption, [ --with-password-encryption=PROVIDER Which cryptographic library to use for encrypting stored passwords (gnutls, old, default)], + pwd_crypto="$withval", pwd_crypto="default") + +if test x"$pwd_crypto" = xdefault; then +dnl ===Default set to "old" for testing, remove the dnls to restore +dnl ===intended functionality. +dnl if test x"$enable_gnutls" = xyes; then +dnl pwd_crypto="gnutls" +dnl else + pwd_crypto="old" +dnl fi +fi + +case $pwd_crypto in + gnutls) + if test x"$enable_gnutls" = xno; then + AC_MSG_ERROR([GnuTLS password encryption requested but GnuTLS is not available.]) + fi + AC_DEFINE(PASSWORD_CRYPTO_GNUTLS, 1, Use GnuTLS for stored password encryption) + ;; + old) + AC_DEFINE(PASSWORD_CRYPTO_OLD, 1, Use old insecure method for stored password encryption) + ;; + *) + AC_MSG_ERROR([Unknown password encryption provider requested.]) + ;; +esac + + dnl ************************ dnl ** GTK user interface ** dnl ************************ @@ -1952,6 +1981,7 @@ echo "NetworkManager : $enable_networkmanager" echo "Manual : $enable_manual" echo "Generic UMPC code : $enable_generic_umpc" echo "Config dir : $ac_cv_with_config_dir" +echo "Password crypto : $pwd_crypto" echo "Plugins" echo " Built:" diff --git a/src/Makefile.am b/src/Makefile.am @@ -176,6 +176,8 @@ claws_mail_SOURCES = \ news_gtk.c \ noticeview.c \ partial_download.c \ + password.c \ + password_gtk.c \ pop.c \ prefs_account.c \ prefs_actions.c \ @@ -291,6 +293,8 @@ claws_mailinclude_HEADERS = \ news_gtk.h \ noticeview.h \ partial_download.h \ + password.h \ + password_gtk.h \ pop.h \ prefs_account.h \ prefs_actions.h \ diff --git a/src/imap.c b/src/imap.c @@ -71,6 +71,7 @@ #include "account.h" #include "tags.h" #include "main.h" +#include "password.h" typedef struct _IMAPFolder IMAPFolder; typedef struct _IMAPSession IMAPSession; @@ -1263,7 +1264,7 @@ static gint imap_session_authenticate(IMAPSession *session, Xstrdup_a(acc_pass, pass, {g_free(pass); return MAILIMAP_NO_ERROR;}); g_free(pass); } else { - acc_pass = account->passwd; + acc_pass = password_decrypt(account->passwd, NULL); } try_again: pass = acc_pass; @@ -1272,8 +1273,11 @@ try_again: tmp_pass = input_dialog_query_password_keep(account->recv_server, account->userid, &(account->session_passwd)); - if (!tmp_pass) + if (!tmp_pass) { + memset(acc_pass, 0, strlen(acc_pass)); + g_free(acc_pass); return MAILIMAP_NO_ERROR; + } Xstrdup_a(pass, tmp_pass, {g_free(tmp_pass); return MAILIMAP_NO_ERROR;}); g_free(tmp_pass); } else if (account->imap_auth_type == IMAP_AUTH_ANON || account->imap_auth_type == IMAP_AUTH_GSSAPI) { @@ -1282,6 +1286,8 @@ try_again: if ((ok = imap_auth(session, account->userid, pass, account->imap_auth_type)) != MAILIMAP_NO_ERROR) { if (!failed && !is_fatal(ok)) { + memset(acc_pass, 0, strlen(acc_pass)); + g_free(acc_pass); acc_pass = NULL; failed = TRUE; if (account->session_passwd != NULL) { @@ -1295,11 +1301,15 @@ try_again: mainwindow_show_error(); } else alertpanel_error_log(_("Couldn't login to IMAP server %s."), account->recv_server); - } + } + g_free(acc_pass); + memset(acc_pass, 0, strlen(acc_pass)); return ok; } + memset(acc_pass, 0, strlen(acc_pass)); + g_free(acc_pass); statuswindow_pop_all(); session->authenticated = TRUE; return MAILIMAP_NO_ERROR; diff --git a/src/inc.c b/src/inc.c @@ -59,6 +59,7 @@ #include "log.h" #include "hooks.h" #include "logwindow.h" +#include "password.h" extern SessionStats session_stats; @@ -576,7 +577,7 @@ static gint inc_start(IncProgressDialog *inc_dialog) /* NOP */; } else if (pop3_session->ac_prefs->passwd) pop3_session->pass = - g_strdup(pop3_session->ac_prefs->passwd); + password_decrypt(pop3_session->ac_prefs->passwd, NULL); else { gchar *pass; diff --git a/src/news.c b/src/news.c @@ -48,6 +48,7 @@ #include "statusbar.h" #include "codeconv.h" #include "utils.h" +#include "password.h" #include "prefs_common.h" #include "prefs_account.h" #include "inputdialog.h" @@ -405,7 +406,7 @@ static Session *news_session_new_for_folder(Folder *folder) if (password_get(userid, ac->nntp_server, "nntp", port, &passwd)) { /* NOP */; } else if (ac->passwd && ac->passwd[0]) - passwd = g_strdup(ac->passwd); + passwd = password_decrypt(ac->passwd, NULL); else passwd = input_dialog_query_password_keep(ac->nntp_server, userid, diff --git a/src/password.c b/src/password.c @@ -0,0 +1,551 @@ +/* + * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2016 The Claws Mail Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "claws-features.h" +#endif + +#ifdef PASSWORD_CRYPTO_GNUTLS +# include <gnutls/gnutls.h> +# include <gnutls/crypto.h> +#endif + +#include <glib.h> +#include <glib/gi18n.h> + +#ifdef G_OS_UNIX +#include <fcntl.h> +#include <unistd.h> +#endif + +#include "common/passcrypt.h" +#include "common/utils.h" +#include "account.h" +#include "alertpanel.h" +#include "inputdialog.h" +#include "password.h" +#include "prefs_common.h" + +#ifndef PASSWORD_CRYPTO_OLD +static gchar *_master_password = NULL; + +static const gchar *master_password() +{ + gchar *input; + gboolean end = FALSE; + + if (!prefs_common_get_prefs()->use_master_password) { + return PASSCRYPT_KEY; + } + + if (_master_password != NULL) { + debug_print("Master password is in memory, offering it.\n"); + return _master_password; + } + + while (!end) { + input = input_dialog_with_invisible(_("Input master password"), + _("Input master password"), NULL); + + if (input == NULL) { + debug_print("Cancel pressed at master password dialog.\n"); + break; + } + + if (master_password_is_correct(input)) { + debug_print("Entered master password seems to be correct, remembering it.\n"); + _master_password = input; + end = TRUE; + } else { + alertpanel_error(_("Incorrect master password.")); + } + } + + return _master_password; +} + +const gboolean master_password_is_set() +{ + if (prefs_common_get_prefs()->master_password_hash == NULL + || strlen(prefs_common_get_prefs()->master_password_hash) == 0) + return FALSE; + + return TRUE; +} + +const gboolean master_password_is_correct(const gchar *input) +{ + gchar *hash; + gchar *stored_hash = prefs_common_get_prefs()->master_password_hash; + const GChecksumType hashtype = G_CHECKSUM_SHA512; + const gssize hashlen = g_checksum_type_get_length(hashtype); + gssize stored_len; + + g_return_val_if_fail(input != NULL, FALSE); + + if (stored_hash == NULL) + return FALSE; + + stored_len = strlen(stored_hash); + g_return_val_if_fail(stored_len == 2*hashlen, FALSE); + + hash = g_compute_checksum_for_string(hashtype, input, -1); + + if (!strncasecmp(hash, stored_hash, stored_len)) { + g_free(hash); + return TRUE; + } + g_free(hash); + + return FALSE; +} + +void master_password_change(const gchar *newp) +{ + gchar *pwd, *newpwd; + const gchar *oldp; + GList *cur; + PrefsAccount *acc; + + oldp = master_password(); + g_return_if_fail(oldp != NULL); + + /* Update master password hash in prefs */ + if (prefs_common_get_prefs()->master_password_hash != NULL) + g_free(prefs_common_get_prefs()->master_password_hash); + + if (newp != NULL) { + debug_print("Storing hash of new master password\n"); + prefs_common_get_prefs()->master_password_hash = + g_compute_checksum_for_string(G_CHECKSUM_SHA512, newp, -1); + } else { + debug_print("Setting master_password_hash to NULL\n"); + prefs_common_get_prefs()->master_password_hash = NULL; + } + + /* Now go over all accounts, reencrypting their passwords using + * the new master password. */ + + if (oldp == NULL) + oldp = PASSCRYPT_KEY; + if (newp == NULL) + newp = PASSCRYPT_KEY; + + debug_print("Reencrypting all account passwords...\n"); + for (cur = account_get_list(); cur != NULL; cur = cur->next) { + acc = (PrefsAccount *)cur->data; + debug_print("account %s\n", acc->account_name); + + /* Password for receiving */ + if (acc->passwd != NULL && strlen(acc->passwd) > 0) { + pwd = password_decrypt(acc->passwd, oldp); + if (pwd == NULL) { + debug_print("failed to decrypt recv password with old master password\n"); + } else { + newpwd = password_encrypt(pwd, newp); + memset(pwd, 0, strlen(pwd)); + g_free(pwd); + if (newpwd == NULL) { + debug_print("failed to encrypt recv password with new master password\n"); + } else { + g_free(acc->passwd); + acc->passwd = newpwd; + } + } + } + + /* Password for sending */ + if (acc->smtp_passwd != NULL && strlen(acc->smtp_passwd) > 0) { + pwd = password_decrypt(acc->smtp_passwd, oldp); + if (pwd == NULL) { + debug_print("failed to decrypt smtp password with old master password\n"); + } else { + newpwd = password_encrypt(pwd, newp); + memset(pwd, 0, strlen(pwd)); + g_free(pwd); + if (newpwd == NULL) { + debug_print("failed to encrypt smtp password with new master password\n"); + } else { + g_free(acc->smtp_passwd); + acc->smtp_passwd = newpwd; + } + } + } + } + + /* If master password is currently in memory (entered by user), + * get rid of it. User will have to enter the new one again. */ + if (_master_password != NULL) { + memset(_master_password, 0, strlen(_master_password)); + g_free(_master_password); + } + _master_password = NULL; +} +#endif + +gchar *password_encrypt_old(const gchar *password) +{ + if (!password || strlen(password) == 0) { + return NULL; + } + + gchar *encrypted = g_strdup(password); + gchar *encoded, *result; + gsize len = strlen(password); + + passcrypt_encrypt(encrypted, len); + encoded = g_base64_encode(encrypted, len); + g_free(encrypted); + result = g_strconcat("!", encoded, NULL); + g_free(encoded); + + return result; +} + +gchar *password_decrypt_old(const gchar *password) +{ + if (!password || strlen(password) == 0) { + return NULL; + } + + if (*password != '!' || strlen(password) < 2) { + return NULL; + } + + gsize len; + gchar *decrypted = g_base64_decode(password + 1, &len); + + passcrypt_decrypt(decrypted, len); + return decrypted; +} + +#ifdef PASSWORD_CRYPTO_GNUTLS +#define BUFSIZE 128 + +gchar *password_encrypt_gnutls(const gchar *password, + const gchar *encryption_password) +{ + /* Another, slightly inferior combination is AES-128-CBC + SHA-256. + * Any block cipher in CBC mode with keysize N and a hash algo with + * digest length 2*N would do. */ + gnutls_cipher_algorithm_t algo = GNUTLS_CIPHER_AES_256_CBC; + gnutls_digest_algorithm_t digest = GNUTLS_DIG_SHA512; + gnutls_cipher_hd_t handle; + gnutls_datum_t key, iv; + int ivlen, keylen, digestlen, blocklen, ret, i; + unsigned char hashbuf[BUFSIZE], *buf, *encbuf, *base, *output; +#ifdef G_OS_UNIX + int rnd; +#endif + + g_return_val_if_fail(password != NULL, NULL); + g_return_val_if_fail(encryption_password != NULL, NULL); + + ivlen = gnutls_cipher_get_iv_size(algo); + keylen = gnutls_cipher_get_key_size(algo); + blocklen = gnutls_cipher_get_block_size(algo); + digestlen = gnutls_hash_get_len(digest); + + /* Prepare key for cipher - first half of hash of passkey XORed with + * the second. */ + memset(&hashbuf, 0, BUFSIZE); + if ((ret = gnutls_hash_fast(digest, encryption_password, + strlen(encryption_password), &hashbuf)) < 0) { + debug_print("Hashing passkey failed: %s\n", gnutls_strerror(ret)); + return NULL; + } + for (i = 0; i < digestlen/2; i++) { + hashbuf[i] = hashbuf[i] ^ hashbuf[i+digestlen/2]; + } + + key.data = malloc(keylen); + memcpy(key.data, &hashbuf, keylen); + key.size = keylen; + +#ifdef G_OS_UNIX + /* Prepare our source of random data. */ + rnd = open("/dev/urandom", O_RDONLY); + if (rnd == -1) { + perror("fopen on /dev/urandom"); + g_free(key.data); + g_free(iv.data); + return NULL; + } +#endif + + /* Prepare random IV for cipher */ + iv.data = malloc(ivlen); + iv.size = ivlen; +#ifdef G_OS_UNIX + ret = read(rnd, iv.data, ivlen); + if (ret != ivlen) { + perror("read into iv"); + g_free(key.data); + g_free(iv.data); + close(rnd); + return NULL; + } +#endif + + /* Initialize the encryption */ + ret = gnutls_cipher_init(&handle, algo, &key, &iv); + if (ret < 0) { + g_free(key.data); + g_free(iv.data); +#ifdef G_OS_UNIX + close(rnd); +#endif + return NULL; + } + + /* Fill buf with one block of random data, our password, pad the + * rest with zero bytes. */ + buf = malloc(BUFSIZE + blocklen); + memset(buf, 0, BUFSIZE); +#ifdef G_OS_UNIX + ret = read(rnd, buf, blocklen); + if (ret != blocklen) { + perror("read into buffer"); + g_free(buf); + g_free(key.data); + g_free(iv.data); + close(rnd); + gnutls_cipher_deinit(handle); + return NULL; + } + + /* We don't need any more random data. */ + close(rnd); +#endif + + memcpy(buf + blocklen, password, strlen(password)); + + /* Encrypt into encbuf */ + encbuf = malloc(BUFSIZE + blocklen); + memset(encbuf, 0, BUFSIZE + blocklen); + ret = gnutls_cipher_encrypt2(handle, buf, BUFSIZE + blocklen, + encbuf, BUFSIZE + blocklen); + if (ret < 0) { + g_free(key.data); + g_free(iv.data); + g_free(buf); + g_free(encbuf); + gnutls_cipher_deinit(handle); + return NULL; + } + + /* Cleanup */ + gnutls_cipher_deinit(handle); + g_free(key.data); + g_free(iv.data); + g_free(buf); + + /* And finally prepare the resulting string: + * "{algorithm}base64encodedciphertext" */ + base = g_base64_encode(encbuf, BUFSIZE); + g_free(encbuf); + output = g_strdup_printf("{%s}%s", gnutls_cipher_get_name(algo), base); + g_free(base); + + return output; +} + +gchar *password_decrypt_gnutls(const gchar *password, + const gchar *decryption_password) +{ + gchar **tokens, *tmp; + gnutls_cipher_algorithm_t algo; + gnutls_digest_algorithm_t digest = GNUTLS_DIG_UNKNOWN; + gnutls_cipher_hd_t handle; + gnutls_datum_t key, iv; + int ivlen, keylen, digestlen, blocklen, ret, i; + gsize len; + unsigned char hashbuf[BUFSIZE], *buf; +#ifdef G_OS_UNIX + int rnd; +#endif + + g_return_val_if_fail(password != NULL, NULL); + g_return_val_if_fail(decryption_password != NULL, NULL); + + tokens = g_strsplit_set(password, "{}", 3); + + /* Parse the string, retrieving algorithm and encrypted data. + * We expect "{algorithm}base64encodedciphertext". */ + if (strlen(tokens[0]) != 0 || + (algo = gnutls_cipher_get_id(tokens[1])) == GNUTLS_CIPHER_UNKNOWN || + strlen(tokens[2]) == 0) + return NULL; + + /* Our hash algo needs to have digest length twice as long as our + * cipher algo's key length. */ + if (algo == GNUTLS_CIPHER_AES_256_CBC) { + debug_print("Using AES-256-CBC + SHA-512 for decryption\n"); + digest = GNUTLS_DIG_SHA512; + } else if (algo == GNUTLS_CIPHER_AES_128_CBC) { + debug_print("Using AES-128-CBC + SHA-256 for decryption\n"); + digest = GNUTLS_DIG_SHA256; + } + if (digest == GNUTLS_DIG_UNKNOWN) { + debug_print("Password is encrypted with unsupported cipher, giving up.\n"); + g_strfreev(tokens); + return NULL; + } + + ivlen = gnutls_cipher_get_iv_size(algo); + keylen = gnutls_cipher_get_key_size(algo); + blocklen = gnutls_cipher_get_block_size(algo); + digestlen = gnutls_hash_get_len(digest); + + /* Prepare key for cipher - first half of hash of passkey XORed with + * the second. AES-256 has key length 32 and length of SHA-512 hash + * is exactly twice that, 64. */ + memset(&hashbuf, 0, BUFSIZE); + if ((ret = gnutls_hash_fast(digest, decryption_password, + strlen(decryption_password), &hashbuf)) < 0) { + debug_print("Hashing passkey failed: %s\n", gnutls_strerror(ret)); + g_strfreev(tokens); + return NULL; + } + for (i = 0; i < digestlen/2; i++) { + hashbuf[i] = hashbuf[i] ^ hashbuf[i+digestlen/2]; + } + + key.data = malloc(keylen); + memcpy(key.data, &hashbuf, keylen); + key.size = keylen; + +#ifdef G_OS_UNIX + /* Prepare our source of random data. */ + rnd = open("/dev/urandom", O_RDONLY); + if (rnd == -1) { + perror("fopen on /dev/urandom"); + g_free(key.data); + g_free(iv.data); + g_strfreev(tokens); + return NULL; + } +#endif + + /* Prepare random IV for cipher */ + iv.data = malloc(ivlen); + iv.size = ivlen; +#ifdef G_OS_UNIX + ret = read(rnd, iv.data, ivlen); + if (ret != ivlen) { + perror("read into iv"); + g_free(key.data); + g_free(iv.data); + g_strfreev(tokens); + close(rnd); + return NULL; + } + + /* We don't need any more random data. */ + close(rnd); +#endif + + /* Prepare encrypted password string for decryption. */ + tmp = g_base64_decode(tokens[2], &len); + g_strfreev(tokens); + + /* Initialize the decryption */ + ret = gnutls_cipher_init(&handle, algo, &key, &iv); + if (ret < 0) { + debug_print("Cipher init failed: %s\n", gnutls_strerror(ret)); + g_free(key.data); + g_free(iv.data); + return NULL; + } + + buf = malloc(BUFSIZE + blocklen); + memset(buf, 0, BUFSIZE + blocklen); + ret = gnutls_cipher_decrypt2(handle, tmp, len, + buf, BUFSIZE + blocklen); + if (ret < 0) { + debug_print("Decryption failed: %s\n", gnutls_strerror(ret)); + g_free(key.data); + g_free(iv.data); + g_free(buf); + gnutls_cipher_deinit(handle); + return NULL; + } + + /* Cleanup */ + gnutls_cipher_deinit(handle); + g_free(key.data); + g_free(iv.data); + + tmp = g_strndup(buf + blocklen, MIN(strlen(buf + blocklen), BUFSIZE)); + g_free(buf); + return tmp; +} + +#undef BUFSIZE + +#endif + +gchar *password_encrypt(const gchar *password, + const gchar *encryption_password) +{ + if (password == NULL || strlen(password) == 0) { + return NULL; + } + +#ifndef PASSWORD_CRYPTO_OLD + if (encryption_password == NULL) + encryption_password = master_password(); + + return password_encrypt_real(password, encryption_password); +#endif + + return password_encrypt_old(password); +} + +gchar *password_decrypt(const gchar *password, + const gchar *decryption_password) +{ + if (password == NULL || strlen(password) == 0) { + return NULL; + } + + /* First, check if the password was possibly decrypted using old, + * obsolete method */ + if (*password == '!') { + debug_print("Trying to decrypt password using the old method...\n"); + return password_decrypt_old(password); + } + + /* Try available crypto backend */ +#ifndef PASSWORD_CRYPTO_OLD + if (decryption_password == NULL) + decryption_password = master_password(); + + if (*password == '{') { + debug_print("Trying to decrypt password...\n"); + return password_decrypt_real(password, decryption_password); + } +#endif + + /* Fallback, in case the configuration is really old and + * stored password in plaintext */ + debug_print("Assuming password was stored plaintext, returning it unchanged\n"); + return g_strdup(password); +} diff --git a/src/password.h b/src/password.h @@ -0,0 +1,63 @@ +/* + * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2016 Claws Mail team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __PASSWORD_H +#define __PASSWORD_H + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#include <glib.h> + +#ifndef PASSWORD_CRYPTO_OLD +const gboolean master_password_is_set(); +const gboolean master_password_is_correct(const gchar *input); +void master_password_change(const gchar *newp); +#endif + +/* Wrapper around the old, DES-CBC-broken implementation which + * returns a newly allocated string for the encrypt/decrypt result. + * This is for compatibility with with the rest of password-related + * functions.*/ +gchar *password_encrypt_old(const gchar *password); +gchar *password_decrypt_old(const gchar *password); + +#ifdef PASSWORD_CRYPTO_GNUTLS +/* GNUTLS implementation */ +gchar *password_encrypt_gnutls(const gchar *password, + const gchar *encryption_password); +gchar *password_decrypt_gnutls(const gchar *password, + const gchar *decryption_password); +#define password_encrypt_real(n, m) password_encrypt_gnutls(n, m) +#define password_decrypt_real(n, m) password_decrypt_gnutls(n, m) +#endif + +/* Wrapper function that will apply best encryption available, + * and return a string ready to be saved as-is in preferences. */ +gchar *password_encrypt(const gchar *password, + const gchar *encryption_password); + +/* This is a wrapper function that looks at the whole string from + * prefs (e.g. including the leading '!' for old implementation), + * and tries to do the smart thing. */ +gchar *password_decrypt(const gchar *password, + const gchar *decryption_password); + +#endif /* __PASSWORD_H */ diff --git a/src/password_gtk.c b/src/password_gtk.c @@ -0,0 +1,212 @@ +/* + * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2016 The Claws Mail Team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#include "claws-features.h" +#endif + +#ifndef PASSWORD_CRYPTO_OLD + +#include <glib.h> +#include <glib/gi18n.h> +#include <gtk/gtk.h> + +#include "common/utils.h" +#include "gtk/manage_window.h" +#include "gtk/gtkutils.h" +#include "account.h" +#include "alertpanel.h" +#include "password.h" +#include "prefs_common.h" + +static void entry_new1_activated(GtkEntry *entry, gpointer user_data) +{ + const gchar *text = gtk_entry_get_text(entry); + + if (strlen(text) > 0) + gtk_widget_grab_focus(GTK_WIDGET(user_data)); +} + +struct _ctx { + gboolean done; + GtkWidget *dialog; + GtkWidget *entry_new1; + GtkWidget *entry_new2; +}; + +static void ok_button_clicked(GtkButton *button, gpointer user_data) +{ + struct _ctx *ctx = (struct _ctx *)user_data; + const gchar *new1 = gtk_entry_get_text(GTK_ENTRY(ctx->entry_new1)); + const gchar *new2 = gtk_entry_get_text(GTK_ENTRY(ctx->entry_new2)); + + debug_print("OK button activated\n"); + + /* Now we check the new password - same in both entries. */ + if (strcmp(new1, new2)) { + debug_print("passwords do not match\n"); + alertpanel_warning(_("New passwords do not match, try again.")); + gtk_entry_set_text(GTK_ENTRY(ctx->entry_new1), ""); + gtk_entry_set_text(GTK_ENTRY(ctx->entry_new2), ""); + gtk_widget_grab_focus(ctx->entry_new1); + return; + } + + master_password_change(new1); + + ctx->done = TRUE; + gtk_widget_destroy(ctx->dialog); + ctx->dialog = NULL; +} + +static void cancel_button_clicked(GtkButton *button, gpointer user_data) +{ + struct _ctx *ctx = (struct _ctx *)user_data; + ctx->done = TRUE; + gtk_widget_destroy(ctx->dialog); + ctx->dialog = NULL; +} + +static void dialog_destroy(GtkWidget *widget, gpointer user_data) +{ + struct _ctx *ctx = (struct _ctx *)user_data; + ctx->done = TRUE; + ctx->dialog = NULL; +} + +void master_password_change_dialog() +{ + static PangoFontDescription *font_desc; + GtkWidget *dialog; + GtkWidget *vbox, *hbox; + GtkWidget *icon; + GtkWidget *msg_title, *msg_label; + GtkWidget *entry_new1, *entry_new2; + GtkWidget *confirm_area; + GtkWidget *ok_button, *cancel_button; + struct _ctx *ctx; + + dialog = gtk_dialog_new(); + + gtk_window_set_resizable(GTK_WINDOW(dialog), FALSE); + gtk_window_set_default_size(GTK_WINDOW(dialog), 375, 100); + gtk_window_set_title(GTK_WINDOW(dialog), ""); + + MANAGE_WINDOW_SIGNALS_CONNECT(dialog); + + vbox = gtk_dialog_get_content_area(GTK_DIALOG(dialog)); + gtk_box_set_spacing(GTK_BOX(vbox), 14); + hbox = gtk_hbox_new(FALSE, 12); + gtk_container_set_border_width(GTK_CONTAINER(hbox), 5); + gtk_widget_show(hbox); + gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0); + + icon = gtk_image_new_from_stock(GTK_STOCK_DIALOG_AUTHENTICATION, + GTK_ICON_SIZE_DIALOG); + gtk_misc_set_alignment(GTK_MISC(icon), 0.5, 0.0); + gtk_box_pack_start(GTK_BOX(hbox), icon, FALSE, FALSE, 0); + + vbox = gtk_vbox_new(FALSE, 12); + gtk_box_pack_start(GTK_BOX(hbox), vbox, TRUE, TRUE, 0); + gtk_widget_show(vbox); + + msg_title = gtk_label_new(_("Changing master password")); + gtk_misc_set_alignment(GTK_MISC(msg_title), 0, 0.5); + gtk_label_set_justify(GTK_LABEL(msg_title), GTK_JUSTIFY_LEFT); + gtk_label_set_use_markup (GTK_LABEL (msg_title), TRUE); + gtk_box_pack_start(GTK_BOX(vbox), msg_title, FALSE, FALSE, 0); + gtk_label_set_line_wrap(GTK_LABEL(msg_title), TRUE); + if (!font_desc) { + gint size; + + size = pango_font_description_get_size + (gtk_widget_get_style(msg_title)->font_desc); + font_desc = pango_font_description_new(); + pango_font_description_set_weight + (font_desc, PANGO_WEIGHT_BOLD); + pango_font_description_set_size + (font_desc, size * PANGO_SCALE_LARGE); + } + if (font_desc) + gtk_widget_modify_font(msg_title, font_desc); + + msg_label = gtk_label_new("<some useful text goes here>"); + gtk_misc_set_alignment(GTK_MISC(msg_label), 0, 0.5); + gtk_label_set_justify(GTK_LABEL(msg_label), GTK_JUSTIFY_LEFT); + gtk_box_pack_start(GTK_BOX(vbox), msg_label, FALSE, FALSE, 0); + gtk_widget_show(msg_label); + + entry_new1 = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entry_new1), FALSE); + gtk_box_pack_start(GTK_BOX(vbox), entry_new1, FALSE, FALSE, 0); + + entry_new2 = gtk_entry_new(); + gtk_entry_set_visibility(GTK_ENTRY(entry_new2), FALSE); + gtk_box_pack_start(GTK_BOX(vbox), entry_new2, FALSE, FALSE, 0); + + gtkut_stock_button_set_create(&confirm_area, + &cancel_button, GTK_STOCK_CANCEL, + &ok_button, GTK_STOCK_OK, + NULL, NULL); + + gtk_box_pack_end(GTK_BOX(gtk_dialog_get_action_area(GTK_DIALOG(dialog))), + confirm_area, FALSE, FALSE, 0); + gtk_container_set_border_width(GTK_CONTAINER(confirm_area), 5); + + gtk_widget_grab_default(ok_button); + + g_signal_connect(G_OBJECT(entry_new1), "activate", + G_CALLBACK(entry_new1_activated), entry_new2); + gtk_entry_set_activates_default(GTK_ENTRY(entry_new2), TRUE); + + ctx = g_new(struct _ctx, 1); + ctx->done = FALSE; + ctx->dialog = dialog; + ctx->entry_new1 = entry_new1; + ctx->entry_new2 = entry_new2; + + g_signal_connect(G_OBJECT(ok_button), "clicked", + G_CALLBACK(ok_button_clicked), ctx); + g_signal_connect(G_OBJECT(cancel_button), "clicked", + G_CALLBACK(cancel_button_clicked), ctx); + + g_signal_connect(G_OBJECT(dialog), "destroy", + G_CALLBACK(dialog_destroy), ctx); + + gtk_widget_show_all(gtk_dialog_get_content_area(GTK_DIALOG(dialog))); + gtk_window_present(GTK_WINDOW(dialog)); + + gtk_window_set_modal(GTK_WINDOW(dialog), TRUE); + manage_window_set_transient(GTK_WINDOW(dialog)); + + while (!ctx->done) + gtk_main_iteration(); + + manage_window_focus_out(dialog, NULL, NULL); + + if (ctx->dialog != NULL) + gtk_widget_destroy(ctx->dialog); + + GTK_EVENTS_FLUSH(); + + g_free(ctx); +} + +#endif /* !PASSWORD_CRYPTO_OLD */ diff --git a/src/password_gtk.h b/src/password_gtk.h @@ -0,0 +1,31 @@ +/* + * Claws Mail -- a GTK+ based, lightweight, and fast e-mail client + * Copyright (C) 2016 Claws Mail team + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +#ifndef __PASSWORD_GTK_H +#define __PASSWORD_GTK_H + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifndef PASSWORD_CRYPTO_OLD +void master_password_change_dialog(); +#endif + +#endif /* __PASSWORD_GTK_H */ diff --git a/src/prefs_common.c b/src/prefs_common.c @@ -18,7 +18,7 @@ */ #ifdef HAVE_CONFIG_H -# include "config.h" +#include "config.h" #include "claws-features.h" #endif @@ -1189,6 +1189,10 @@ static PrefParam param[] = { {"address_search_wildcard", "TRUE", &prefs_common.address_search_wildcard, P_BOOL, NULL, NULL, NULL}, {"enable_avatars", "3", &prefs_common.enable_avatars, P_INT, NULL, NULL, NULL}, +#ifndef PASSWORD_CRYPTO_OLD + {"use_master_password", FALSE, &prefs_common.use_master_password, P_BOOL, NULL, NULL, NULL }, + {"master_password_hash", "", &prefs_common.master_password_hash, P_STRING, NULL, NULL, NULL }, +#endif {NULL, NULL, NULL, P_OTHER, NULL, NULL, NULL} }; diff --git a/src/prefs_common.h b/src/prefs_common.h @@ -21,6 +21,7 @@ #define __PREFS_COMMON_H__ #ifdef HAVE_CONFIG_H +#include "config.h" #include "claws-features.h" #endif @@ -537,6 +538,11 @@ struct _PrefsCommon gboolean address_search_wildcard; guint enable_avatars; + +#ifndef PASSWORD_CRYPTO_OLD + gboolean use_master_password; + gchar *master_password_hash; +#endif }; extern PrefsCommon prefs_common; diff --git a/src/prefs_gtk.c b/src/prefs_gtk.c @@ -40,7 +40,7 @@ #include "prefs_common.h" #include "utils.h" #include "gtkutils.h" -#include "passcrypt.h" +#include "password.h" #include "codeconv.h" #define CL(x) (((gulong) (x) >> (gulong) 8) & 0xFFUL) @@ -169,6 +169,7 @@ static void prefs_config_parse_one_line(PrefParam *param, const gchar *buf) switch (param[i].type) { case P_STRING: + case P_PASSWORD: { gchar *tmp = NULL; @@ -216,23 +217,6 @@ static void prefs_config_parse_one_line(PrefParam *param, const gchar *buf) /* be compatible and accept ints */ *((gulong *)param[i].data) = strtoul(value, 0, 10); break; - case P_PASSWORD: - g_free(*((gchar **)param[i].data)); - if (value[0] == '!') { - gchar *tmp; - gsize len; - - tmp = g_base64_decode(&value[1], &len); - passcrypt_decrypt(tmp, len); - - *((gchar **)param[i].data) = - *tmp ? g_strdup(tmp) : NULL; - g_free(tmp); - } else { - *((gchar **)param[i].data) = - *value ? g_strdup(value) : NULL; - } - break; default: break; } @@ -340,6 +324,7 @@ gint prefs_write_param(PrefParam *param, FILE *fp) for (i = 0; param[i].name != NULL; i++) { switch (param[i].type) { case P_STRING: + case P_PASSWORD: { gchar *tmp = NULL; @@ -381,27 +366,6 @@ gint prefs_write_param(PrefParam *param, FILE *fp) g_snprintf(buf, sizeof buf, "%s=#%6.6lx\n", param[i].name, *((gulong *) param[i].data)); break; - case P_PASSWORD: - { - gchar *tmp = NULL, *tmp2 = NULL; - - tmp = *((gchar **)param[i].data); - if (tmp) { - gint len; - - tmp = g_strdup(tmp); - len = strlen(tmp); - passcrypt_encrypt(tmp, len); - tmp2 = g_base64_encode(tmp, len); - g_free(tmp); - tmp = tmp2; - } - g_snprintf(buf, sizeof(buf), "%s=!%s\n", param[i].name, - tmp ? - tmp : ""); - g_free(tmp); - } - break; default: /* unrecognized, fail */ debug_print("Unrecognized parameter type\n"); @@ -662,7 +626,6 @@ void prefs_set_data_from_entry(PrefParam *pparam) switch (pparam->type) { case P_STRING: - case P_PASSWORD: str = (gchar **)pparam->data; g_free(*str); *str = entry_str[0] ? g_strdup(entry_str) : NULL; @@ -673,6 +636,11 @@ void prefs_set_data_from_entry(PrefParam *pparam) case P_INT: *((gint *)pparam->data) = atoi(entry_str); break; + case P_PASSWORD: + str = (gchar **)pparam->data; + g_free(*str); + *str = password_encrypt(entry_str, NULL); + break; default: g_warning("Invalid PrefType for GtkEntry widget: %d", pparam->type); @@ -705,7 +673,6 @@ void prefs_set_entry(PrefParam *pparam) switch (pparam->type) { case P_STRING: - case P_PASSWORD: str = (gchar **)pparam->data; gtk_entry_set_text(GTK_ENTRY(*pparam->widget), *str ? *str : ""); @@ -718,6 +685,11 @@ void prefs_set_entry(PrefParam *pparam) gtk_entry_set_text(GTK_ENTRY(*pparam->widget), itos(*((gushort *)pparam->data))); break; + case P_PASSWORD: + str = (gchar **)pparam->data; + gtk_entry_set_text(GTK_ENTRY(*pparam->widget), + password_decrypt(*str, NULL)); + break; default: g_warning("Invalid PrefType for GtkEntry widget: %d", pparam->type); diff --git a/src/prefs_other.c b/src/prefs_other.c @@ -17,7 +17,7 @@ */ #ifdef HAVE_CONFIG_H -# include "config.h" +#include "config.h" #include "claws-features.h" #endif @@ -37,6 +37,10 @@ #include "gtk/gtkutils.h" #include "gtk/prefswindow.h" #include "combobox.h" +#ifndef PASSWORD_CRYPTO_OLD +#include "password.h" +#include "password_gtk.h" +#endif #include "manage_window.h" #ifdef HAVE_LIBETPAN @@ -61,6 +65,9 @@ typedef struct _OtherPage GtkWidget *checkbtn_real_time_sync; GtkWidget *flush_metadata_faster_radiobtn; GtkWidget *flush_metadata_safer_radiobtn; +#ifndef PASSWORD_CRYPTO_OLD + GtkWidget *checkbtn_use_password; +#endif } OtherPage; static struct KeybindDialog { @@ -77,6 +84,10 @@ static gboolean prefs_keybind_key_pressed (GtkWidget *widget, gpointer data); static void prefs_keybind_cancel (void); static void prefs_keybind_apply_clicked (GtkWidget *widget); +#ifndef PASSWORD_CRYPTO_OLD +static void prefs_change_master_password(GtkButton *button, gpointer data); +static void prefs_use_password_toggled(GtkToggleButton *button, gpointer data); +#endif static void prefs_keybind_select(void) @@ -465,6 +476,13 @@ static void prefs_other_create_widget(PrefsPage *_page, GtkWindow *window, GtkWidget *flush_metadata_faster_radiobtn; GtkWidget *flush_metadata_safer_radiobtn; +#ifndef PASSWORD_CRYPTO_OLD + GtkWidget *vbox_password; + GtkWidget *frame_password; + GtkWidget *checkbtn_use_password; + GtkWidget *button_change_password; +#endif + gchar *shred_binary = NULL; vbox1 = gtk_vbox_new (FALSE, VSPACING); @@ -585,6 +603,31 @@ static void prefs_other_create_widget(PrefsPage *_page, GtkWindow *window, PACK_CHECK_BUTTON (vbox2, checkbtn_real_time_sync, _("Synchronise offline folders as soon as possible")); +#ifndef PASSWORD_CRYPTO_OLD + vbox_password = gtkut_get_options_frame(vbox1, &frame_password, _("Master password")); + + PACK_CHECK_BUTTON(vbox_password, checkbtn_use_password, + _("Use a master password")); + + CLAWS_SET_TIP(checkbtn_use_password, + _("If checked, your saved account passwords will be protected " + "by a master password. If no master password is set, " + "you will be prompted to set one.")); + + button_change_password = gtk_button_new_with_label( + _("Change master password")); + gtk_widget_show (button_change_password); + hbox1 = gtk_hbox_new (FALSE, 8); + gtk_widget_show (hbox1); + gtk_box_pack_start (GTK_BOX (vbox_password), hbox1, FALSE, FALSE, 0); + gtk_box_pack_start (GTK_BOX (hbox1), button_change_password, + FALSE, FALSE, 0); + g_signal_connect (G_OBJECT (checkbtn_use_password), "toggled", + G_CALLBACK (prefs_use_password_toggled), button_change_password); + g_signal_connect (G_OBJECT (button_change_password), "clicked", + G_CALLBACK (prefs_change_master_password), NULL); +#endif + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_addaddrbyclick), prefs_common.add_address_by_click); gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_confonexit), @@ -608,6 +651,13 @@ static void prefs_other_create_widget(PrefsPage *_page, GtkWindow *window, gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_real_time_sync), prefs_common.real_time_sync); +#ifndef PASSWORD_CRYPTO_OLD + gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(checkbtn_use_password), + prefs_common.use_master_password); + gtk_widget_set_sensitive(button_change_password, + prefs_common.use_master_password); +#endif + prefs_other->checkbtn_addaddrbyclick = checkbtn_addaddrbyclick; prefs_other->checkbtn_confonexit = checkbtn_confonexit; prefs_other->checkbtn_cleanonexit = checkbtn_cleanonexit; @@ -620,6 +670,9 @@ static void prefs_other_create_widget(PrefsPage *_page, GtkWindow *window, prefs_other->checkbtn_real_time_sync = checkbtn_real_time_sync; prefs_other->flush_metadata_safer_radiobtn = flush_metadata_safer_radiobtn; prefs_other->flush_metadata_faster_radiobtn = flush_metadata_faster_radiobtn; +#ifndef PASSWORD_CRYPTO_OLD + prefs_other->checkbtn_use_password = checkbtn_use_password; +#endif prefs_other->page.widget = vbox1; } @@ -654,7 +707,27 @@ static void prefs_other_save(PrefsPage *_page) GTK_TOGGLE_BUTTON(page->checkbtn_use_shred)); prefs_common.real_time_sync = gtk_toggle_button_get_active( - GTK_TOGGLE_BUTTON(page->checkbtn_real_time_sync)); + GTK_TOGGLE_BUTTON(page->checkbtn_real_time_sync)); + +#ifndef PASSWORD_CRYPTO_OLD + /* If we're disabling use of master password, we need to reencrypt + * all account passwords with hardcoded key. */ + if (!gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(page->checkbtn_use_password)) + && master_password_is_set()) { + master_password_change(NULL); + } + + if (gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(page->checkbtn_use_password)) + && !master_password_is_set()) { + master_password_change_dialog(); + } + + prefs_common.use_master_password = + gtk_toggle_button_get_active( + GTK_TOGGLE_BUTTON(page->checkbtn_use_password)); +#endif gtk_can_change_accels = gtk_toggle_button_get_active( GTK_TOGGLE_BUTTON(page->checkbtn_gtk_can_change_accels)); @@ -709,3 +782,20 @@ void prefs_other_done(void) prefs_gtk_unregister_page((PrefsPage *) prefs_other); g_free(prefs_other); } + +#ifndef PASSWORD_CRYPTO_OLD +void prefs_change_master_password(GtkButton *button, gpointer data) +{ + /* Call the password change dialog */ + master_password_change_dialog(); +} + +void prefs_use_password_toggled(GtkToggleButton *chkbtn, gpointer data) +{ + GtkWidget *button = GTK_WIDGET(data); + gboolean active = gtk_toggle_button_get_active(chkbtn); + + if (!active) + gtk_widget_set_sensitive(button, active); +} +#endif diff --git a/src/send_message.c b/src/send_message.c @@ -54,6 +54,7 @@ #include "gtkutils.h" #include "inc.h" #include "log.h" +#include "password.h" typedef struct _SendProgressDialog SendProgressDialog; @@ -305,7 +306,7 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g /* NOP */; } else if (ac_prefs->smtp_passwd) smtp_session->pass = - g_strdup(ac_prefs->smtp_passwd); + password_decrypt(ac_prefs->smtp_passwd, NULL); else { smtp_session->pass = input_dialog_query_password_keep @@ -324,7 +325,7 @@ gint send_message_smtp_full(PrefsAccount *ac_prefs, GSList *to_list, FILE *fp, g &(smtp_session->pass))) { /* NOP */; } else if (ac_prefs->passwd) - smtp_session->pass = g_strdup(ac_prefs->passwd); + smtp_session->pass = password_decrypt(ac_prefs->passwd, NULL); else { smtp_session->pass = input_dialog_query_password_keep diff --git a/src/wizard.c b/src/wizard.c @@ -50,6 +50,7 @@ #endif #include "prefs_common.h" #include "combobox.h" +#include "password.h" typedef enum { @@ -756,13 +757,13 @@ static gboolean wizard_write_config(WizardWindow *wizard) prefs_account->userid = g_strdup( gtk_entry_get_text(GTK_ENTRY(wizard->recv_username))); - prefs_account->passwd = g_strdup( - gtk_entry_get_text(GTK_ENTRY(wizard->recv_password))); + prefs_account->passwd = password_encrypt( + gtk_entry_get_text(GTK_ENTRY(wizard->recv_password)), NULL); prefs_account->smtp_userid = g_strdup( gtk_entry_get_text(GTK_ENTRY(wizard->smtp_username))); - prefs_account->smtp_passwd = g_strdup( - gtk_entry_get_text(GTK_ENTRY(wizard->smtp_password))); + prefs_account->smtp_passwd = password_encrypt( + gtk_entry_get_text(GTK_ENTRY(wizard->smtp_password)), NULL); if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(wizard->smtp_auth))) { prefs_account->use_smtp_auth = TRUE; }