NeoMutt  2023-03-22-27-g3cb248
Teaching an old dog new tricks
DOXYGEN
auth.c File Reference

POP authentication. More...

#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include <string.h>
#include "private.h"
#include "mutt/lib.h"
#include "address/lib.h"
#include "config/lib.h"
#include "core/lib.h"
#include "conn/lib.h"
#include "adata.h"
+ Include dependency graph for auth.c:

Go to the source code of this file.

Functions

void pop_apop_timestamp (struct PopAccountData *adata, char *buf)
 Get the server timestamp for APOP authentication. More...
 
static enum PopAuthRes pop_auth_apop (struct PopAccountData *adata, const char *method)
 APOP authenticator - Implements PopAuth::authenticate() More...
 
static enum PopAuthRes pop_auth_user (struct PopAccountData *adata, const char *method)
 USER authenticator - Implements PopAuth::authenticate() More...
 
static enum PopAuthRes pop_auth_oauth (struct PopAccountData *adata, const char *method)
 Authenticate a POP connection using OAUTHBEARER - Implements PopAuth::authenticate() More...
 
bool pop_auth_is_valid (const char *authenticator)
 Check if string is a valid pop authentication method. More...
 
int pop_authenticate (struct PopAccountData *adata)
 Authenticate with a POP server. More...
 

Variables

static const struct PopAuth PopAuthenticators []
 Accepted authentication methods. More...
 

Detailed Description

POP authentication.

Authors
  • Vsevolod Volkov
  • Richard Russon

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/.

Definition in file auth.c.

Function Documentation

◆ pop_apop_timestamp()

void pop_apop_timestamp ( struct PopAccountData adata,
char *  buf 
)

Get the server timestamp for APOP authentication.

Parameters
adataPOP Account data
bufTimestamp string

Definition at line 300 of file auth.c.

301{
302 char *p1 = NULL, *p2 = NULL;
303
304 FREE(&adata->timestamp);
305
306 if ((p1 = strchr(buf, '<')) && (p2 = strchr(p1, '>')))
307 {
308 p2[1] = '\0';
309 adata->timestamp = mutt_str_dup(p1);
310 }
311}
#define FREE(x)
Definition: memory.h:43
char * mutt_str_dup(const char *str)
Copy a string, safely.
Definition: string.c:250
char * timestamp
Definition: adata.h:54
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pop_auth_apop()

static enum PopAuthRes pop_auth_apop ( struct PopAccountData adata,
const char *  method 
)
static

APOP authenticator - Implements PopAuth::authenticate()

Definition at line 316 of file auth.c.

317{
318 struct Md5Ctx md5ctx;
319 unsigned char digest[16];
320 char hash[33] = { 0 };
321 char buf[1024] = { 0 };
322
323 if (mutt_account_getpass(&adata->conn->account) || !adata->conn->account.pass[0])
324 return POP_A_FAILURE;
325
326 if (!adata->timestamp)
327 return POP_A_UNAVAIL;
328
329 if (!mutt_addr_valid_msgid(adata->timestamp))
330 {
331 mutt_error(_("POP timestamp is invalid"));
332 return POP_A_UNAVAIL;
333 }
334
335 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
336 mutt_message(_("Authenticating (%s)..."), "APOP");
337
338 /* Compute the authentication hash to send to the server */
339 mutt_md5_init_ctx(&md5ctx);
340 mutt_md5_process(adata->timestamp, &md5ctx);
341 mutt_md5_process(adata->conn->account.pass, &md5ctx);
342 mutt_md5_finish_ctx(&md5ctx, digest);
343 mutt_md5_toascii(digest, hash);
344
345 /* Send APOP command to server */
346 snprintf(buf, sizeof(buf), "APOP %s %s\r\n", adata->conn->account.user, hash);
347
348 switch (pop_query(adata, buf, sizeof(buf)))
349 {
350 case 0:
351 return POP_A_SUCCESS;
352 case -1:
353 return POP_A_SOCKET;
354 }
355
356 // L10N: %s is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
357 mutt_error(_("%s authentication failed"), "APOP");
358
359 return POP_A_FAILURE;
360}
bool mutt_addr_valid_msgid(const char *msgid)
Is this a valid Message ID?
Definition: address.c:772
int mutt_account_getpass(struct ConnAccount *cac)
Fetch password into ConnAccount, if necessary.
Definition: connaccount.c:129
#define mutt_error(...)
Definition: logging.h:87
#define mutt_message(...)
Definition: logging.h:86
void mutt_md5_process(const char *str, struct Md5Ctx *md5ctx)
Process a NULL-terminated string.
Definition: md5.c:356
void mutt_md5_init_ctx(struct Md5Ctx *md5ctx)
Initialise the MD5 computation.
Definition: md5.c:262
void * mutt_md5_finish_ctx(struct Md5Ctx *md5ctx, void *resbuf)
Process the remaining bytes in the buffer.
Definition: md5.c:286
void mutt_md5_toascii(const void *digest, char *resbuf)
Convert a binary MD5 digest into ASCII Hexadecimal.
Definition: md5.c:457
#define _(a)
Definition: message.h:28
@ POP_A_UNAVAIL
No valid authentication method.
Definition: private.h:62
@ POP_A_SUCCESS
Authenticated successfully.
Definition: private.h:59
@ POP_A_FAILURE
Authentication failed.
Definition: private.h:61
@ POP_A_SOCKET
Connection lost.
Definition: private.h:60
#define pop_query(adata, buf, buflen)
Definition: private.h:109
char user[128]
Username.
Definition: connaccount.h:56
char pass[256]
Password.
Definition: connaccount.h:57
struct ConnAccount account
Account details: username, password, etc.
Definition: connection.h:50
Cursor for the MD5 hashing.
Definition: md5.h:37
struct Connection * conn
Connection to POP server.
Definition: adata.h:38
+ Here is the call graph for this function:

◆ pop_auth_user()

static enum PopAuthRes pop_auth_user ( struct PopAccountData adata,
const char *  method 
)
static

USER authenticator - Implements PopAuth::authenticate()

Definition at line 365 of file auth.c.

366{
367 if (!adata->cmd_user)
368 return POP_A_UNAVAIL;
369
370 if (mutt_account_getpass(&adata->conn->account) || !adata->conn->account.pass[0])
371 return POP_A_FAILURE;
372
373 mutt_message(_("Logging in..."));
374
375 char buf[1024] = { 0 };
376 snprintf(buf, sizeof(buf), "USER %s\r\n", adata->conn->account.user);
377 int rc = pop_query(adata, buf, sizeof(buf));
378
379 if (adata->cmd_user == 2)
380 {
381 if (rc == 0)
382 {
383 adata->cmd_user = 1;
384
385 mutt_debug(LL_DEBUG1, "set USER capability\n");
386 }
387
388 if (rc == -2)
389 {
390 adata->cmd_user = 0;
391
392 mutt_debug(LL_DEBUG1, "unset USER capability\n");
393 snprintf(adata->err_msg, sizeof(adata->err_msg), "%s",
394 _("Command USER is not supported by server"));
395 }
396 }
397
398 if (rc == 0)
399 {
400 snprintf(buf, sizeof(buf), "PASS %s\r\n", adata->conn->account.pass);
401 const short c_debug_level = cs_subset_number(NeoMutt->sub, "debug_level");
402 rc = pop_query_d(adata, buf, sizeof(buf),
403 /* don't print the password unless we're at the ungodly debugging level */
404 (c_debug_level < MUTT_SOCK_LOG_FULL) ? "PASS *\r\n" : NULL);
405 }
406
407 switch (rc)
408 {
409 case 0:
410 return POP_A_SUCCESS;
411 case -1:
412 return POP_A_SOCKET;
413 }
414
415 mutt_error("%s %s", _("Login failed"), adata->err_msg);
416
417 return POP_A_FAILURE;
418}
short cs_subset_number(const struct ConfigSubset *sub, const char *name)
Get a number config item by name.
Definition: helpers.c:169
#define mutt_debug(LEVEL,...)
Definition: logging.h:84
@ LL_DEBUG1
Log at debug level 1.
Definition: logging.h:40
int pop_query_d(struct PopAccountData *adata, char *buf, size_t buflen, char *msg)
Send data from buffer and receive answer to the same buffer.
Definition: lib.c:457
#define MUTT_SOCK_LOG_FULL
Definition: socket.h:56
Container for Accounts, Notifications.
Definition: neomutt.h:37
struct ConfigSubset * sub
Inherited config items.
Definition: neomutt.h:39
char err_msg[POP_CMD_RESPONSE]
Definition: adata.h:56
unsigned int cmd_user
optional command USER
Definition: adata.h:44
+ Here is the call graph for this function:

◆ pop_auth_oauth()

static enum PopAuthRes pop_auth_oauth ( struct PopAccountData adata,
const char *  method 
)
static

Authenticate a POP connection using OAUTHBEARER - Implements PopAuth::authenticate()

Definition at line 423 of file auth.c.

424{
425 /* If they did not explicitly request or configure oauth then fail quietly */
426 const char *const c_pop_oauth_refresh_command = cs_subset_string(NeoMutt->sub, "pop_oauth_refresh_command");
427 if (!method && !c_pop_oauth_refresh_command)
428 return POP_A_UNAVAIL;
429
430 // L10N: (%s) is the method name, e.g. Anonymous, CRAM-MD5, GSSAPI, SASL
431 mutt_message(_("Authenticating (%s)..."), "OAUTHBEARER");
432
433 char *oauthbearer = mutt_account_getoauthbearer(&adata->conn->account, false);
434 if (!oauthbearer)
435 return POP_A_FAILURE;
436
437 size_t auth_cmd_len = strlen(oauthbearer) + 30;
438 char *auth_cmd = mutt_mem_malloc(auth_cmd_len);
439 snprintf(auth_cmd, auth_cmd_len, "AUTH OAUTHBEARER %s\r\n", oauthbearer);
440 FREE(&oauthbearer);
441
442 int rc = pop_query_d(adata, auth_cmd, strlen(auth_cmd),
443#ifdef DEBUG
444 /* don't print the bearer token unless we're at the ungodly debugging level */
445 (cs_subset_number(NeoMutt->sub, "debug_level") < MUTT_SOCK_LOG_FULL) ?
446 "AUTH OAUTHBEARER *\r\n" :
447#endif
448 NULL);
449 FREE(&auth_cmd);
450
451 switch (rc)
452 {
453 case 0:
454 return POP_A_SUCCESS;
455 case -1:
456 return POP_A_SOCKET;
457 }
458
459 /* The error response was a SASL continuation, so "continue" it.
460 * See RFC7628 3.2.3 */
461 mutt_socket_send(adata->conn, "\001");
462
463 char *err = adata->err_msg;
464 char decoded_err[1024] = { 0 };
465 int len = mutt_b64_decode(adata->err_msg, decoded_err, sizeof(decoded_err) - 1);
466 if (len >= 0)
467 {
468 decoded_err[len] = '\0';
469 err = decoded_err;
470 }
471 mutt_error("%s %s", _("Authentication failed"), err);
472
473 return POP_A_FAILURE;
474}
int mutt_b64_decode(const char *in, char *out, size_t olen)
Convert null-terminated base64 string to raw bytes.
Definition: base64.c:136
const char * cs_subset_string(const struct ConfigSubset *sub, const char *name)
Get a string config item by name.
Definition: helpers.c:317
char * mutt_account_getoauthbearer(struct ConnAccount *cac, bool xoauth2)
Get an OAUTHBEARER/XOAUTH2 token.
Definition: connaccount.c:194
void * mutt_mem_malloc(size_t size)
Allocate memory on the heap.
Definition: memory.c:90
#define mutt_socket_send(conn, buf)
Definition: socket.h:59
+ Here is the call graph for this function:

◆ pop_auth_is_valid()

bool pop_auth_is_valid ( const char *  authenticator)

Check if string is a valid pop authentication method.

Parameters
authenticatorAuthenticator string to check
Return values
trueArgument is a valid auth method

Validate whether an input string is an accepted pop authentication method as defined by PopAuthenticators.

Definition at line 502 of file auth.c.

503{
504 for (size_t i = 0; i < mutt_array_size(PopAuthenticators); i++)
505 {
506 const struct PopAuth *auth = &PopAuthenticators[i];
507 if (auth->method && mutt_istr_equal(auth->method, authenticator))
508 return true;
509 }
510
511 return false;
512}
#define mutt_array_size(x)
Definition: memory.h:36
bool mutt_istr_equal(const char *a, const char *b)
Compare two strings, ignoring case.
Definition: string.c:819
static const struct PopAuth PopAuthenticators[]
Accepted authentication methods.
Definition: auth.c:479
POP authentication multiplexor.
Definition: private.h:78
const char * method
Name of authentication method supported, NULL means variable.
Definition: private.h:87
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

◆ pop_authenticate()

int pop_authenticate ( struct PopAccountData adata)

Authenticate with a POP server.

Parameters
adataPOP Account data
Return values
numResult, e.g. POP_A_SUCCESS
0Successful
-1Connection lost
-2Login failed
-3Authentication cancelled

Definition at line 523 of file auth.c.

524{
525 struct ConnAccount *cac = &adata->conn->account;
526 const struct PopAuth *authenticator = NULL;
527 int attempts = 0;
528 int rc = POP_A_UNAVAIL;
529
530 if ((mutt_account_getuser(cac) < 0) || (cac->user[0] == '\0'))
531 {
532 return -3;
533 }
534
535 const struct Slist *c_pop_authenticators = cs_subset_slist(NeoMutt->sub, "pop_authenticators");
536 const bool c_pop_auth_try_all = cs_subset_bool(NeoMutt->sub, "pop_auth_try_all");
537 if (c_pop_authenticators && (c_pop_authenticators->count > 0))
538 {
539 /* Try user-specified list of authentication methods */
540 struct ListNode *np = NULL;
541 STAILQ_FOREACH(np, &c_pop_authenticators->head, entries)
542 {
543 mutt_debug(LL_DEBUG2, "Trying method %s\n", np->data);
544 authenticator = PopAuthenticators;
545
546 while (authenticator->authenticate)
547 {
548 if (!authenticator->method || mutt_istr_equal(authenticator->method, np->data))
549 {
550 rc = authenticator->authenticate(adata, np->data);
551 if (rc == POP_A_SOCKET)
552 {
553 switch (pop_connect(adata))
554 {
555 case 0:
556 {
557 rc = authenticator->authenticate(adata, np->data);
558 break;
559 }
560 case -2:
561 rc = POP_A_FAILURE;
562 }
563 }
564
565 if (rc != POP_A_UNAVAIL)
566 attempts++;
567 if ((rc == POP_A_SUCCESS) || (rc == POP_A_SOCKET) ||
568 ((rc == POP_A_FAILURE) && !c_pop_auth_try_all))
569 {
570 break;
571 }
572 }
573 authenticator++;
574 }
575 }
576 }
577 else
578 {
579 /* Fall back to default: any authenticator */
580 mutt_debug(LL_DEBUG2, "Using any available method\n");
581 authenticator = PopAuthenticators;
582
583 while (authenticator->authenticate)
584 {
585 rc = authenticator->authenticate(adata, NULL);
586 if (rc == POP_A_SOCKET)
587 {
588 switch (pop_connect(adata))
589 {
590 case 0:
591 {
592 rc = authenticator->authenticate(adata, NULL);
593 break;
594 }
595 case -2:
596 rc = POP_A_FAILURE;
597 }
598 }
599
600 if (rc != POP_A_UNAVAIL)
601 attempts++;
602 if ((rc == POP_A_SUCCESS) || (rc == POP_A_SOCKET) ||
603 ((rc == POP_A_FAILURE) && !c_pop_auth_try_all))
604 {
605 break;
606 }
607
608 authenticator++;
609 }
610 }
611
612 switch (rc)
613 {
614 case POP_A_SUCCESS:
615 return 0;
616 case POP_A_SOCKET:
617 return -1;
618 case POP_A_UNAVAIL:
619 if (attempts == 0)
620 mutt_error(_("No authenticators available"));
621 }
622
623 return -2;
624}
const struct Slist * cs_subset_slist(const struct ConfigSubset *sub, const char *name)
Get a string-list config item by name.
Definition: helpers.c:268
bool cs_subset_bool(const struct ConfigSubset *sub, const char *name)
Get a boolean config item by name.
Definition: helpers.c:73
int mutt_account_getuser(struct ConnAccount *cac)
Retrieve username into ConnAccount, if necessary.
Definition: connaccount.c:49
@ LL_DEBUG2
Log at debug level 2.
Definition: logging.h:41
int pop_connect(struct PopAccountData *adata)
Open connection.
Definition: lib.c:273
#define STAILQ_FOREACH(var, head, field)
Definition: queue.h:352
Login details for a remote server.
Definition: connaccount.h:53
A List node for strings.
Definition: list.h:35
char * data
String.
Definition: list.h:36
enum PopAuthRes(* authenticate)(struct PopAccountData *adata, const char *method)
Authenticate a POP connection.
Definition: private.h:85
String list.
Definition: slist.h:47
struct ListHead head
List containing values.
Definition: slist.h:48
size_t count
Number of values in list.
Definition: slist.h:49
+ Here is the call graph for this function:
+ Here is the caller graph for this function:

Variable Documentation

◆ PopAuthenticators

const struct PopAuth PopAuthenticators[]
static
Initial value:
= {
{ pop_auth_oauth, "oauthbearer" },
{ pop_auth_apop, "apop" },
{ pop_auth_user, "user" },
{ NULL, NULL },
}
static enum PopAuthRes pop_auth_user(struct PopAccountData *adata, const char *method)
USER authenticator - Implements PopAuth::authenticate()
Definition: auth.c:365
static enum PopAuthRes pop_auth_apop(struct PopAccountData *adata, const char *method)
APOP authenticator - Implements PopAuth::authenticate()
Definition: auth.c:316
static enum PopAuthRes pop_auth_oauth(struct PopAccountData *adata, const char *method)
Authenticate a POP connection using OAUTHBEARER - Implements PopAuth::authenticate()
Definition: auth.c:423

Accepted authentication methods.

Definition at line 479 of file auth.c.