-
Richard Russon committed 2 years ago1 parent 9c456f52
Revision indexing in progress... (symbol navigation in revisions will be accurate after indexed)
-
-
-
-
-
-
1 + /** 2 + * @file 3 + * GNU SASL authentication support 4 + * 5 + * @authors 6 + * Copyright (C) 2022 Richard Russon <[email protected]> 7 + * 8 + * @copyright 9 + * This program is free software: you can redistribute it and/or modify it under 10 + * the terms of the GNU General Public License as published by the Free Software 11 + * Foundation, either version 2 of the License, or (at your option) any later 12 + * version. 13 + * 14 + * This program is distributed in the hope that it will be useful, but WITHOUT 15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 + * details. 18 + * 19 + * You should have received a copy of the GNU General Public License along with 20 + * this program. If not, see <http://www.gnu.org/licenses/>. 21 + */ 22 + 23 + /** 24 + * @page conn_gsasl GNU SASL authentication support 25 + * 26 + * GNU SASL authentication support 27 + */ 28 + 29 + #include "config.h" 30 + #include <gsasl.h> 31 + #include <stdbool.h> 32 + #include <string.h> 33 + #include "mutt/lib.h" 34 + #include "connaccount.h" 35 + #include "connection.h" 36 + #include "gsasl2.h" 37 + #include "mutt_account.h" 38 + 39 + static Gsasl *mutt_gsasl_ctx = NULL; 40 + 41 + /** 42 + * mutt_gsasl_callback - Callback to retrieve authname or user from ConnAccount 43 + * @param ctx GNU SASL context 44 + * @param sctx GNU SASL session 45 + * @param prop Property to get, e.g. GSASL_PASSWORD 46 + * @retval num GNU SASL error code, e.g. GSASL_OK 47 + */ 48 + static int mutt_gsasl_callback(Gsasl *ctx, Gsasl_session *sctx, Gsasl_property prop) 49 + { 50 + int rc = GSASL_NO_CALLBACK; 51 + 52 + struct Connection *conn = gsasl_session_hook_get(sctx); 53 + if (!conn) 54 + { 55 + mutt_debug(LL_DEBUG1, "missing session hook data!\n"); 56 + return rc; 57 + } 58 + 59 + switch (prop) 60 + { 61 + case GSASL_PASSWORD: 62 + if (mutt_account_getpass(&conn->account)) 63 + return rc; 64 + gsasl_property_set(sctx, GSASL_PASSWORD, conn->account.pass); 65 + rc = GSASL_OK; 66 + break; 67 + 68 + case GSASL_AUTHID: 69 + /* whom the provided password belongs to: login */ 70 + if (mutt_account_getlogin(&conn->account)) 71 + return rc; 72 + gsasl_property_set(sctx, GSASL_AUTHID, conn->account.login); 73 + rc = GSASL_OK; 74 + break; 75 + 76 + case GSASL_AUTHZID: 77 + /* name of the user whose mail/resources you intend to access: user */ 78 + if (mutt_account_getuser(&conn->account)) 79 + return rc; 80 + gsasl_property_set(sctx, GSASL_AUTHZID, conn->account.user); 81 + rc = GSASL_OK; 82 + break; 83 + 84 + case GSASL_ANONYMOUS_TOKEN: 85 + gsasl_property_set(sctx, GSASL_ANONYMOUS_TOKEN, "dummy"); 86 + rc = GSASL_OK; 87 + break; 88 + 89 + case GSASL_SERVICE: 90 + { 91 + const char *service = NULL; 92 + switch (conn->account.type) 93 + { 94 + case MUTT_ACCT_TYPE_IMAP: 95 + service = "imap"; 96 + break; 97 + case MUTT_ACCT_TYPE_POP: 98 + service = "pop"; 99 + break; 100 + case MUTT_ACCT_TYPE_SMTP: 101 + service = "smtp"; 102 + break; 103 + default: 104 + return rc; 105 + } 106 + gsasl_property_set(sctx, GSASL_SERVICE, service); 107 + rc = GSASL_OK; 108 + break; 109 + } 110 + 111 + case GSASL_HOSTNAME: 112 + gsasl_property_set(sctx, GSASL_HOSTNAME, conn->account.host); 113 + rc = GSASL_OK; 114 + break; 115 + 116 + default: 117 + break; 118 + } 119 + 120 + return rc; 121 + } 122 + 123 + /** 124 + * mutt_gsasl_init - Initialise GNU SASL library 125 + * @retval true Success 126 + */ 127 + static bool mutt_gsasl_init(void) 128 + { 129 + if (mutt_gsasl_ctx) 130 + return true; 131 + 132 + int rc = gsasl_init(&mutt_gsasl_ctx); 133 + if (rc != GSASL_OK) 134 + { 135 + mutt_gsasl_ctx = NULL; 136 + mutt_debug(LL_DEBUG1, "libgsasl initialisation failed (%d): %s.\n", rc, 137 + gsasl_strerror(rc)); 138 + return false; 139 + } 140 + 141 + gsasl_callback_set(mutt_gsasl_ctx, mutt_gsasl_callback); 142 + return true; 143 + } 144 + 145 + /** 146 + * mutt_gsasl_done - Shutdown GNU SASL library 147 + */ 148 + void mutt_gsasl_done(void) 149 + { 150 + if (!mutt_gsasl_ctx) 151 + return; 152 + 153 + gsasl_done(mutt_gsasl_ctx); 154 + mutt_gsasl_ctx = NULL; 155 + } 156 + 157 + /** 158 + * mutt_gsasl_get_mech - Pick a connection mechanism 159 + * @param requested_mech Requested mechanism 160 + * @param server_mechlist Server's list of mechanisms 161 + * @retval ptr Selected mechanism string 162 + */ 163 + const char *mutt_gsasl_get_mech(const char *requested_mech, const char *server_mechlist) 164 + { 165 + if (!mutt_gsasl_init()) 166 + return NULL; 167 + 168 + /* libgsasl does not do case-independent string comparisons, 169 + * and stores its methods internally in uppercase. */ 170 + char *uc_server_mechlist = mutt_str_dup(server_mechlist); 171 + if (uc_server_mechlist) 172 + mutt_str_upper(uc_server_mechlist); 173 + 174 + char *uc_requested_mech = mutt_str_dup(requested_mech); 175 + if (uc_requested_mech) 176 + mutt_str_upper(uc_requested_mech); 177 + 178 + const char *sel_mech = NULL; 179 + if (uc_requested_mech) 180 + sel_mech = gsasl_client_suggest_mechanism(mutt_gsasl_ctx, uc_requested_mech); 181 + else 182 + sel_mech = gsasl_client_suggest_mechanism(mutt_gsasl_ctx, uc_server_mechlist); 183 + 184 + FREE(&uc_requested_mech); 185 + FREE(&uc_server_mechlist); 186 + 187 + return sel_mech; 188 + } 189 + 190 + /** 191 + * mutt_gsasl_client_new - Create a new GNU SASL client 192 + * @param conn Connection to a server 193 + * @param mech Mechanisms to use 194 + * @param sctx GNU SASL Session 195 + * @retval 0 Success 196 + * @retval -1 Error 197 + */ 198 + int mutt_gsasl_client_new(struct Connection *conn, const char *mech, Gsasl_session **sctx) 199 + { 200 + if (!mutt_gsasl_init()) 201 + return -1; 202 + 203 + int rc = gsasl_client_start(mutt_gsasl_ctx, mech, sctx); 204 + if (rc != GSASL_OK) 205 + { 206 + *sctx = NULL; 207 + mutt_debug(LL_DEBUG1, "gsasl_client_start failed (%d): %s.\n", rc, gsasl_strerror(rc)); 208 + return -1; 209 + } 210 + 211 + gsasl_session_hook_set(*sctx, conn); 212 + return 0; 213 + } 214 + 215 + /** 216 + * mutt_gsasl_client_finish - Free a GNU SASL client 217 + * @param sctx GNU SASL Session 218 + */ 219 + void mutt_gsasl_client_finish(Gsasl_session **sctx) 220 + { 221 + gsasl_finish(*sctx); 222 + *sctx = NULL; 223 + } 224 + -
1 + /** 2 + * @file 3 + * GNU SASL authentication support 4 + * 5 + * @authors 6 + * Copyright (C) 2022 Richard Russon <[email protected]> 7 + * 8 + * @copyright 9 + * This program is free software: you can redistribute it and/or modify it under 10 + * the terms of the GNU General Public License as published by the Free Software 11 + * Foundation, either version 2 of the License, or (at your option) any later 12 + * version. 13 + * 14 + * This program is distributed in the hope that it will be useful, but WITHOUT 15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 + * details. 18 + * 19 + * You should have received a copy of the GNU General Public License along with 20 + * this program. If not, see <http://www.gnu.org/licenses/>. 21 + */ 22 + 23 + #ifndef MUTT_CONN_GSASL_H 24 + #define MUTT_CONN_GSASL_H 25 + 26 + #include <gsasl.h> 27 + 28 + struct Connection; 29 + 30 + void mutt_gsasl_client_finish(Gsasl_session **sctx); 31 + int mutt_gsasl_client_new (struct Connection *conn, const char *mech, Gsasl_session **sctx); 32 + void mutt_gsasl_done (void); 33 + const char *mutt_gsasl_get_mech (const char *requested_mech, const char *server_mechlist); 34 + 35 + #endif /* MUTT_CONN_GSASL_H */ 36 + -
-
-
-
1 + /** 2 + * @file 3 + * IMAP GNU SASL authentication method 4 + * 5 + * @authors 6 + * Copyright (C) 2022 Richard Russon <[email protected]> 7 + * 8 + * @copyright 9 + * This program is free software: you can redistribute it and/or modify it under 10 + * the terms of the GNU General Public License as published by the Free Software 11 + * Foundation, either version 2 of the License, or (at your option) any later 12 + * version. 13 + * 14 + * This program is distributed in the hope that it will be useful, but WITHOUT 15 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 16 + * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more 17 + * details. 18 + * 19 + * You should have received a copy of the GNU General Public License along with 20 + * this program. If not, see <http://www.gnu.org/licenses/>. 21 + */ 22 + 23 + /** 24 + * @page imap_auth_gsasl GNU SASL authentication 25 + * 26 + * IMAP GNU SASL authentication method 27 + */ 28 + 29 + #include "config.h" 30 + #include <stddef.h> 31 + #include <gsasl.h> 32 + #include "private.h" 33 + #include "mutt/lib.h" 34 + #include "conn/lib.h" 35 + #include "adata.h" 36 + #include "auth.h" 37 + 38 + /** 39 + * imap_auth_gsasl - GNU SASL authenticator - Implements ImapAuth::authenticate() 40 + */ 41 + enum ImapAuthRes imap_auth_gsasl(struct ImapAccountData *adata, const char *method) 42 + { 43 + Gsasl_session *gsasl_session = NULL; 44 + struct Buffer *output_buf = NULL; 45 + char *imap_step_output = NULL; 46 + int rc = IMAP_AUTH_FAILURE; 47 + int gsasl_rc = GSASL_OK; 48 + int imap_step_rc = IMAP_RES_CONTINUE; 49 + 50 + const char *chosen_mech = mutt_gsasl_get_mech(method, adata->capstr); 51 + if (!chosen_mech) 52 + { 53 + mutt_debug(LL_DEBUG2, "mutt_gsasl_get_mech() returned no usable mech\n"); 54 + return IMAP_AUTH_UNAVAIL; 55 + } 56 + 57 + mutt_debug(LL_DEBUG2, "using mech %s\n", chosen_mech); 58 + 59 + if (mutt_gsasl_client_new(adata->conn, chosen_mech, &gsasl_session) < 0) 60 + { 61 + mutt_debug(LL_DEBUG1, "Error allocating GSASL connection.\n"); 62 + return IMAP_AUTH_UNAVAIL; 63 + } 64 + 65 + mutt_message(_("Authenticating (%s)..."), chosen_mech); 66 + 67 + output_buf = mutt_buffer_pool_get(); 68 + mutt_buffer_printf(output_buf, "AUTHENTICATE %s", chosen_mech); 69 + if (adata->capabilities & IMAP_CAP_SASL_IR) 70 + { 71 + char *gsasl_step_output = NULL; 72 + gsasl_rc = gsasl_step64(gsasl_session, "", &gsasl_step_output); 73 + if ((gsasl_rc != GSASL_NEEDS_MORE) && (gsasl_rc != GSASL_OK)) 74 + { 75 + mutt_debug(LL_DEBUG1, "gsasl_step64() failed (%d): %s\n", gsasl_rc, 76 + gsasl_strerror(gsasl_rc)); 77 + rc = IMAP_AUTH_UNAVAIL; 78 + goto bail; 79 + } 80 + 81 + mutt_buffer_addch(output_buf, ' '); 82 + mutt_buffer_addstr(output_buf, gsasl_step_output); 83 + gsasl_free(gsasl_step_output); 84 + } 85 + imap_cmd_start(adata, mutt_buffer_string(output_buf)); 86 + 87 + do 88 + { 89 + do 90 + { 91 + imap_step_rc = imap_cmd_step(adata); 92 + } while (imap_step_rc == IMAP_RES_CONTINUE); 93 + 94 + if ((imap_step_rc == IMAP_RES_BAD) || (imap_step_rc == IMAP_RES_NO)) 95 + goto bail; 96 + 97 + if (imap_step_rc != IMAP_RES_RESPOND) 98 + break; 99 + 100 + imap_step_output = imap_next_word(adata->buf); 101 + 102 + char *gsasl_step_output = NULL; 103 + gsasl_rc = gsasl_step64(gsasl_session, imap_step_output, &gsasl_step_output); 104 + if ((gsasl_rc == GSASL_NEEDS_MORE) || (gsasl_rc == GSASL_OK)) 105 + { 106 + mutt_buffer_strcpy(output_buf, gsasl_step_output); 107 + gsasl_free(gsasl_step_output); 108 + } 109 + else 110 + { 111 + // sasl error occured, send an abort string 112 + mutt_debug(LL_DEBUG1, "gsasl_step64() failed (%d): %s\n", gsasl_rc, 113 + gsasl_strerror(gsasl_rc)); 114 + mutt_buffer_strcpy(output_buf, "*"); 115 + } 116 + 117 + mutt_buffer_addstr(output_buf, "\r\n"); 118 + mutt_socket_send(adata->conn, mutt_buffer_string(output_buf)); 119 + } while ((gsasl_rc == GSASL_NEEDS_MORE) || (gsasl_rc == GSASL_OK)); 120 + 121 + if (imap_step_rc != IMAP_RES_OK) 122 + { 123 + do 124 + imap_step_rc = imap_cmd_step(adata); 125 + while (imap_step_rc == IMAP_RES_CONTINUE); 126 + } 127 + 128 + if (imap_step_rc == IMAP_RES_RESPOND) 129 + { 130 + mutt_socket_send(adata->conn, "*\r\n"); 131 + goto bail; 132 + } 133 + 134 + if ((gsasl_rc != GSASL_OK) || (imap_step_rc != IMAP_RES_OK)) 135 + goto bail; 136 + 137 + if (imap_code(adata->buf)) 138 + rc = IMAP_AUTH_SUCCESS; 139 + 140 + bail: 141 + mutt_buffer_pool_release(&output_buf); 142 + mutt_gsasl_client_finish(&gsasl_session); 143 + 144 + if (rc == IMAP_AUTH_FAILURE) 145 + { 146 + mutt_debug(LL_DEBUG2, "%s failed\n", chosen_mech); 147 + mutt_error(_("SASL authentication failed")); 148 + } 149 + 150 + return rc; 151 + } 152 + -
-
-
-
-