mutt stable branch with some hacks
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

at jcs 501 lines 12 kB view raw
1/* 2 * Copyright (C) 2000-2001 Vsevolod Volkov <vvv@mutt.org.ua> 3 * 4 * This program is free software; you can redistribute it and/or modify 5 * it under the terms of the GNU General Public License as published by 6 * the Free Software Foundation; either version 2 of the License, or 7 * (at your option) any later version. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * GNU General Public License for more details. 13 * 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 17 */ 18 19#if HAVE_CONFIG_H 20# include "config.h" 21#endif 22 23#include "mutt.h" 24#include "mx.h" 25#include "md5.h" 26#include "pop.h" 27 28#include <string.h> 29#include <unistd.h> 30 31#ifdef USE_SASL 32#include <sasl/sasl.h> 33#include <sasl/saslutil.h> 34 35#include "mutt_sasl.h" 36#endif 37 38#ifdef USE_SASL 39/* SASL authenticator */ 40static pop_auth_res_t pop_auth_sasl (POP_DATA *pop_data, const char *method) 41{ 42 sasl_conn_t *saslconn; 43 sasl_interact_t *interaction = NULL; 44 int rc; 45 char *buf = NULL; 46 size_t bufsize = 0; 47 char inbuf[LONG_STRING]; 48 const char* mech; 49 const char *pc = NULL; 50 unsigned int len, olen, client_start; 51 52 if (mutt_account_getpass (&pop_data->conn->account) || 53 !pop_data->conn->account.pass[0]) 54 return POP_A_FAILURE; 55 56 if (mutt_sasl_client_new (pop_data->conn, &saslconn) < 0) 57 { 58 dprint (1, (debugfile, "pop_auth_sasl: Error allocating SASL connection.\n")); 59 return POP_A_FAILURE; 60 } 61 62 if (!method) 63 method = pop_data->auth_list; 64 65 FOREVER 66 { 67 rc = sasl_client_start(saslconn, method, &interaction, &pc, &olen, &mech); 68 if (rc != SASL_INTERACT) 69 break; 70 mutt_sasl_interact (interaction); 71 } 72 73 if (rc != SASL_OK && rc != SASL_CONTINUE) 74 { 75 dprint (1, (debugfile, "pop_auth_sasl: Failure starting authentication exchange. No shared mechanisms?\n")); 76 77 /* SASL doesn't support suggested mechanisms, so fall back */ 78 sasl_dispose (&saslconn); 79 return POP_A_UNAVAIL; 80 } 81 82 /* About client_start: If sasl_client_start() returns data via pc/olen, 83 * the client is expected to send this first (after the AUTH string is sent). 84 * sasl_client_start() may in fact return SASL_OK in this case. 85 */ 86 client_start = olen; 87 88 mutt_message _("Authenticating (SASL)..."); 89 90 bufsize = ((olen * 2) > LONG_STRING) ? (olen * 2) : LONG_STRING; 91 buf = safe_malloc (bufsize); 92 93 snprintf (buf, bufsize, "AUTH %s", mech); 94 olen = strlen (buf); 95 96 /* looping protocol */ 97 FOREVER 98 { 99 strfcpy (buf + olen, "\r\n", bufsize - olen); 100 mutt_socket_write (pop_data->conn, buf); 101 if (mutt_socket_readln (inbuf, sizeof (inbuf), pop_data->conn) < 0) 102 { 103 sasl_dispose (&saslconn); 104 pop_data->status = POP_DISCONNECTED; 105 FREE (&buf); 106 return POP_A_SOCKET; 107 } 108 109 /* Note we don't exit if rc==SASL_OK when client_start is true. 110 * This is because the first loop has only sent the AUTH string, we 111 * need to loop at least once more to send the pc/olen returned 112 * by sasl_client_start(). 113 */ 114 if (!client_start && rc != SASL_CONTINUE) 115 break; 116 117 if (!mutt_strncmp (inbuf, "+ ", 2) 118 && sasl_decode64 (inbuf+2, strlen (inbuf+2), buf, bufsize - 1, &len) != SASL_OK) 119 { 120 dprint (1, (debugfile, "pop_auth_sasl: error base64-decoding server response.\n")); 121 goto bail; 122 } 123 124 if (!client_start) 125 FOREVER 126 { 127 rc = sasl_client_step (saslconn, buf, len, &interaction, &pc, &olen); 128 if (rc != SASL_INTERACT) 129 break; 130 mutt_sasl_interact (interaction); 131 } 132 else 133 { 134 olen = client_start; 135 client_start = 0; 136 } 137 138 /* Even if sasl_client_step() returns SASL_OK, we should send at 139 * least one more line to the server. See #3862. 140 */ 141 if (rc != SASL_CONTINUE && rc != SASL_OK) 142 break; 143 144 /* send out response, or line break if none needed */ 145 if (pc) 146 { 147 if ((olen * 2) > bufsize) 148 { 149 bufsize = olen * 2; 150 safe_realloc (&buf, bufsize); 151 } 152 if (sasl_encode64 (pc, olen, buf, bufsize, &olen) != SASL_OK) 153 { 154 dprint (1, (debugfile, "pop_auth_sasl: error base64-encoding client response.\n")); 155 goto bail; 156 } 157 } 158 } 159 160 if (rc != SASL_OK) 161 goto bail; 162 163 if (!mutt_strncmp (inbuf, "+OK", 3)) 164 { 165 mutt_sasl_setup_conn (pop_data->conn, saslconn); 166 FREE (&buf); 167 return POP_A_SUCCESS; 168 } 169 170bail: 171 sasl_dispose (&saslconn); 172 173 /* terminate SASL session if the last response is not +OK nor -ERR */ 174 if (!mutt_strncmp (inbuf, "+ ", 2)) 175 { 176 snprintf (buf, bufsize, "*\r\n"); 177 if (pop_query (pop_data, buf, sizeof (buf)) == -1) 178 { 179 FREE (&buf); 180 return POP_A_SOCKET; 181 } 182 } 183 184 FREE (&buf); 185 mutt_error _("SASL authentication failed."); 186 mutt_sleep (2); 187 188 return POP_A_FAILURE; 189} 190#endif 191 192/* Get the server timestamp for APOP authentication */ 193void pop_apop_timestamp (POP_DATA *pop_data, char *buf) 194{ 195 char *p1, *p2; 196 197 FREE (&pop_data->timestamp); 198 199 if ((p1 = strchr (buf, '<')) && (p2 = strchr (p1, '>'))) 200 { 201 p2[1] = '\0'; 202 pop_data->timestamp = safe_strdup (p1); 203 } 204} 205 206/* APOP authenticator */ 207static pop_auth_res_t pop_auth_apop (POP_DATA *pop_data, const char *method) 208{ 209 struct md5_ctx ctx; 210 unsigned char digest[16]; 211 char hash[33]; 212 char buf[LONG_STRING]; 213 size_t i; 214 215 if (mutt_account_getpass (&pop_data->conn->account) || 216 !pop_data->conn->account.pass[0]) 217 return POP_A_FAILURE; 218 219 if (!pop_data->timestamp) 220 return POP_A_UNAVAIL; 221 222 if (rfc822_valid_msgid (pop_data->timestamp) < 0) 223 { 224 mutt_error _("POP timestamp is invalid!"); 225 mutt_sleep (2); 226 return POP_A_UNAVAIL; 227 } 228 229 mutt_message _("Authenticating (APOP)..."); 230 231 /* Compute the authentication hash to send to the server */ 232 md5_init_ctx (&ctx); 233 md5_process_bytes (pop_data->timestamp, strlen (pop_data->timestamp), &ctx); 234 md5_process_bytes (pop_data->conn->account.pass, 235 strlen (pop_data->conn->account.pass), &ctx); 236 md5_finish_ctx (&ctx, digest); 237 238 for (i = 0; i < sizeof (digest); i++) 239 sprintf (hash + 2 * i, "%02x", digest[i]); 240 241 /* Send APOP command to server */ 242 snprintf (buf, sizeof (buf), "APOP %s %s\r\n", pop_data->conn->account.user, hash); 243 244 switch (pop_query (pop_data, buf, sizeof (buf))) 245 { 246 case 0: 247 return POP_A_SUCCESS; 248 case -1: 249 return POP_A_SOCKET; 250 } 251 252 mutt_error _("APOP authentication failed."); 253 mutt_sleep (2); 254 255 return POP_A_FAILURE; 256} 257 258/* USER authenticator */ 259static pop_auth_res_t pop_auth_user (POP_DATA *pop_data, const char *method) 260{ 261 char buf[LONG_STRING]; 262 int ret; 263 264 if (!pop_data->cmd_user) 265 return POP_A_UNAVAIL; 266 267 if (mutt_account_getpass (&pop_data->conn->account) || 268 !pop_data->conn->account.pass[0]) 269 return POP_A_FAILURE; 270 271 mutt_message _("Logging in..."); 272 273 snprintf (buf, sizeof (buf), "USER %s\r\n", pop_data->conn->account.user); 274 ret = pop_query (pop_data, buf, sizeof (buf)); 275 276 if (pop_data->cmd_user == 2) 277 { 278 if (ret == 0) 279 { 280 pop_data->cmd_user = 1; 281 282 dprint (1, (debugfile, "pop_auth_user: set USER capability\n")); 283 } 284 285 if (ret == -2) 286 { 287 pop_data->cmd_user = 0; 288 289 dprint (1, (debugfile, "pop_auth_user: unset USER capability\n")); 290 snprintf (pop_data->err_msg, sizeof (pop_data->err_msg), "%s", 291 _("Command USER is not supported by server.")); 292 } 293 } 294 295 if (ret == 0) 296 { 297 snprintf (buf, sizeof (buf), "PASS %s\r\n", pop_data->conn->account.pass); 298 ret = pop_query_d (pop_data, buf, sizeof (buf), 299#ifdef DEBUG 300 /* don't print the password unless we're at the ungodly debugging level */ 301 debuglevel < MUTT_SOCK_LOG_FULL ? "PASS *\r\n" : 302#endif 303 NULL); 304 } 305 306 switch (ret) 307 { 308 case 0: 309 return POP_A_SUCCESS; 310 case -1: 311 return POP_A_SOCKET; 312 } 313 314 mutt_error ("%s %s", _("Login failed."), pop_data->err_msg); 315 mutt_sleep (2); 316 317 return POP_A_FAILURE; 318} 319 320/* OAUTHBEARER authenticator */ 321static pop_auth_res_t pop_auth_oauth (POP_DATA *pop_data, const char *method) 322{ 323 char *oauthbearer = NULL; 324 char decoded_err[LONG_STRING]; 325 char *err = NULL; 326 char *auth_cmd = NULL; 327 size_t auth_cmd_len; 328 int ret, len; 329 330 /* If they did not explicitly request or configure oauth then fail quietly */ 331 if (!(method || PopOauthRefreshCmd)) 332 return POP_A_UNAVAIL; 333 334 mutt_message _("Authenticating (OAUTHBEARER)..."); 335 336 oauthbearer = mutt_account_getoauthbearer (&pop_data->conn->account); 337 if (oauthbearer == NULL) 338 return POP_A_FAILURE; 339 340 auth_cmd_len = strlen (oauthbearer) + 30; 341 auth_cmd = safe_malloc (auth_cmd_len); 342 snprintf (auth_cmd, auth_cmd_len, "AUTH OAUTHBEARER %s\r\n", oauthbearer); 343 FREE (&oauthbearer); 344 345 ret = pop_query_d (pop_data, auth_cmd, strlen (auth_cmd), 346#ifdef DEBUG 347 /* don't print the bearer token unless we're at the ungodly debugging level */ 348 debuglevel < MUTT_SOCK_LOG_FULL ? "AUTH OAUTHBEARER *\r\n" : 349#endif 350 NULL); 351 FREE (&auth_cmd); 352 353 switch (ret) 354 { 355 case 0: 356 return POP_A_SUCCESS; 357 case -1: 358 return POP_A_SOCKET; 359 } 360 361 /* The error response was a SASL continuation, so "continue" it. 362 * See RFC 7628 3.2.3 363 */ 364 mutt_socket_write (pop_data->conn, "\001"); 365 366 err = pop_data->err_msg; 367 len = mutt_from_base64 (decoded_err, pop_data->err_msg, sizeof(decoded_err) - 1); 368 if (len >= 0) 369 { 370 decoded_err[len] = '\0'; 371 err = decoded_err; 372 } 373 mutt_error ("%s %s", _("Authentication failed."), err); 374 mutt_sleep (2); 375 376 return POP_A_FAILURE; 377} 378 379static const pop_auth_t pop_authenticators[] = { 380 { pop_auth_oauth, "oauthbearer" }, 381#ifdef USE_SASL 382 { pop_auth_sasl, NULL }, 383#endif 384 { pop_auth_apop, "apop" }, 385 { pop_auth_user, "user" }, 386 { NULL, NULL } 387}; 388 389/* 390 * Authentication 391 * 0 - successful, 392 * -1 - connection lost, 393 * -2 - login failed, 394 * -3 - authentication canceled. 395 */ 396int pop_authenticate (POP_DATA* pop_data) 397{ 398 ACCOUNT *acct = &pop_data->conn->account; 399 const pop_auth_t* authenticator; 400 char* methods; 401 char* comma; 402 char* method; 403 int attempts = 0; 404 int ret = POP_A_UNAVAIL; 405 406 if (mutt_account_getuser (acct) || !acct->user[0]) 407 return -3; 408 409 if (PopAuthenticators) 410 { 411 /* Try user-specified list of authentication methods */ 412 methods = safe_strdup (PopAuthenticators); 413 method = methods; 414 415 while (method) 416 { 417 comma = strchr (method, ':'); 418 if (comma) 419 *comma++ = '\0'; 420 dprint (2, (debugfile, "pop_authenticate: Trying method %s\n", method)); 421 authenticator = pop_authenticators; 422 423 while (authenticator->authenticate) 424 { 425 if (!authenticator->method || 426 !ascii_strcasecmp (authenticator->method, method)) 427 { 428 ret = authenticator->authenticate (pop_data, method); 429 if (ret == POP_A_SOCKET) 430 switch (pop_connect (pop_data)) 431 { 432 case 0: 433 { 434 ret = authenticator->authenticate (pop_data, method); 435 break; 436 } 437 case -2: 438 ret = POP_A_FAILURE; 439 } 440 441 if (ret != POP_A_UNAVAIL) 442 attempts++; 443 if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET || 444 (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) 445 { 446 comma = NULL; 447 break; 448 } 449 } 450 authenticator++; 451 } 452 453 method = comma; 454 } 455 456 FREE (&methods); 457 } 458 else 459 { 460 /* Fall back to default: any authenticator */ 461 dprint (2, (debugfile, "pop_authenticate: Using any available method.\n")); 462 authenticator = pop_authenticators; 463 464 while (authenticator->authenticate) 465 { 466 ret = authenticator->authenticate (pop_data, NULL); 467 if (ret == POP_A_SOCKET) 468 switch (pop_connect (pop_data)) 469 { 470 case 0: 471 { 472 ret = authenticator->authenticate (pop_data, NULL); 473 break; 474 } 475 case -2: 476 ret = POP_A_FAILURE; 477 } 478 479 if (ret != POP_A_UNAVAIL) 480 attempts++; 481 if (ret == POP_A_SUCCESS || ret == POP_A_SOCKET || 482 (ret == POP_A_FAILURE && !option (OPTPOPAUTHTRYALL))) 483 break; 484 485 authenticator++; 486 } 487 } 488 489 switch (ret) 490 { 491 case POP_A_SUCCESS: 492 return 0; 493 case POP_A_SOCKET: 494 return -1; 495 case POP_A_UNAVAIL: 496 if (!attempts) 497 mutt_error (_("No authenticators available")); 498 } 499 500 return -2; 501}