From: Ken Hornstein Date: Thu, 20 Oct 2016 17:37:29 +0000 (-0400) Subject: Rototill credentials code so that we only prompt for the username and X-Git-Url: https://diplodocus.org/git/nmh/commitdiff_plain/90edb255effd0d29d94e662ca5bf3e9eda7ed122?ds=sidebyside;hp=bed6327e0962c95394b530b7b1ebc40e2af28320 Rototill credentials code so that we only prompt for the username and password when it is necessary. --- diff --git a/h/mh.h b/h/mh.h index 3c2fb061..55825da0 100644 --- a/h/mh.h +++ b/h/mh.h @@ -396,12 +396,7 @@ typedef struct m_getfld_state *m_getfld_state_t; /* * credentials management */ -struct nmh_creds { - char *host; - char *user; - char *password; -}; - +struct nmh_creds; typedef struct nmh_creds *nmh_creds_t; /* diff --git a/h/popsbr.h b/h/popsbr.h index c155fd12..96d21a33 100644 --- a/h/popsbr.h +++ b/h/popsbr.h @@ -3,8 +3,8 @@ * popsbr.h -- header for POP client subroutines */ -int pop_init (char *, char *, char *, char *, char *, int, int, char *, int, - const char *); +int pop_init (char *, char *, char *, char *, int, int, char *, int, + const char *); int pop_fd (char *, int, char *, int); int pop_stat (int *, int *); int pop_retr (int, int (*)(char *)); diff --git a/h/prototypes.h b/h/prototypes.h index f06322b5..a17d12f9 100644 --- a/h/prototypes.h +++ b/h/prototypes.h @@ -325,7 +325,23 @@ char *pwd (void); char *r1bindex(char *, int); void readconfig (struct node **, FILE *, const char *, int); int refile (char **, char *); -void ruserpass (char *, char **, char **); + +/* + * Read our credentials file and (optionally) ask the user for anything + * missing. + * + * Arguments: + * + * host - Hostname (to scan credentials file) + * aname - Pointer to filled-in username + * apass - Pointer to filled-in password + * flags - One or more of RUSERPASS_NO_PROMPT_USER, + * RUSERPASS_NO_PROMPT_PASSWORD + */ +void ruserpass (const char *host, char **aname, char **apass, int flags); +#define RUSERPASS_NO_PROMPT_USER 0x01 +#define RUSERPASS_NO_PROMPT_PASSWORD 0x02 + int remdir (char *); void scan_detect_mbox_style (FILE *); void scan_finished (); @@ -497,7 +513,46 @@ void hexify (const unsigned char *, size_t, char **); * credentials management */ void init_credentials_file (); -int nmh_get_credentials (char *, char *, int, nmh_creds_t); + +/* + * Allocate and return a credentials structure. The credentials structure + * is now opaque; you need to use accessors to get inside of it. The + * accessors will only prompt the user for missing fields if they are + * needed. + * + * Arguments: + * + * host - Hostname we're connecting to (used to search credentials file) + * user - Username we are logging in as; can be NULL. + * + * Returns NULL on error, otherwise an allocated nmh_creds structure. + */ +nmh_creds_t nmh_get_credentials (const char *host, const char *user); + +/* + * Retrieve the user from a nmh_creds structure. May prompt the user + * if one is not defined. + * + * Arguments: + * + * creds - Structure from previous nmh_get_credentials() call + * + * Returns NULL on error, otherwise a NUL-termined string containing + * the username. Points to allocated memory in the credentials structure + * that is free()d by nmh_free_credentials(). + */ +const char *nmh_cred_get_user(nmh_creds_t creds); + +/* + * Retrieve the password from an nmh_creds structure. Otherwise identical + * to nmh_cred_get_user(). + */ +const char *nmh_cred_get_password(nmh_creds_t creds); + +/* + * Free an allocated nmh_creds structure. + */ +void nmh_credentials_free(nmh_creds_t creds); /* * program initialization diff --git a/sbr/credentials.c b/sbr/credentials.c index 9b5dd537..9000e5ed 100644 --- a/sbr/credentials.c +++ b/sbr/credentials.c @@ -8,6 +8,12 @@ #include #include +struct nmh_creds { + char *host; /* Hostname corresponding to credentials */ + char *user; /* Username corresponding to credentials */ + char *pass; /* (Optional) password used by credentials */ +}; + void init_credentials_file () { if (credentials_file == NULL) { @@ -44,21 +50,23 @@ init_credentials_file () { } } -int -nmh_get_credentials (char *host, char *user, int sasl, nmh_creds_t creds) { +nmh_creds_t +nmh_get_credentials (const char *host, const char *user) +{ + nmh_creds_t creds; + char *cred_style = context_find ("credentials"); init_credentials_file (); - creds->host = host; - if (cred_style == NULL || ! strcmp (cred_style, "legacy")) { - creds->user = user == NULL ? getusername () : user; - if (sasl) { + creds = mh_xmalloc(sizeof(*creds)); - /* This is what inc.c and msgchk.c used to contain. */ - /* Only inc.c and msgchk.c do this. smtp.c doesn't. */ - creds->password = getusername (); - } + creds->host = getcpy(host); + creds->user = NULL; + creds->pass = NULL; + + if (cred_style == NULL || ! strcmp (cred_style, "legacy")) { + creds->user = user == NULL ? getcpy(getusername ()) : getcpy(user); } else if (! strncasecmp (cred_style, "file:", 5) || ! strncasecmp (cred_style, "file-nopermcheck:", 17)) { /* @@ -69,12 +77,64 @@ nmh_get_credentials (char *host, char *user, int sasl, nmh_creds_t creds) { * 3) interactively request from user (as long as the * credentials file didn't have a "default" token) */ - creds->user = user; + creds->user = user == NULL ? NULL : getcpy(user); } else { admonish (NULL, "unknown credentials style %s", cred_style); - return NOTOK; + return NULL; + } + + ruserpass(creds->host, &creds->user, &creds->pass, + RUSERPASS_NO_PROMPT_USER | RUSERPASS_NO_PROMPT_PASSWORD); + + return creds; +} + +/* + * Retrieve the username + */ + +const char * +nmh_cred_get_user(nmh_creds_t creds) +{ + if (! creds->user) { + ruserpass(creds->host, &creds->user, &creds->pass, + RUSERPASS_NO_PROMPT_PASSWORD); + } + + return creds->user; +} + +/* + * Retrieve the password + */ + +const char * +nmh_cred_get_password(nmh_creds_t creds) +{ + if (! creds->pass) { + ruserpass(creds->host, &creds->user, &creds->pass, 0); + } + + return creds->pass; +} + +/* + * Free our credentials + */ + +void +nmh_credentials_free(nmh_creds_t creds) +{ + if (creds->host) + free(creds->host); + + if (creds->user) + free(creds->user); + + if (creds->pass) { + memset(creds->pass, 0, strlen(creds->pass)); + free(creds->pass); } - ruserpass (host, &creds->user, &creds->password); - return OK; + free(creds); } diff --git a/sbr/netsec.c b/sbr/netsec.c index 788b23bf..990b2318 100644 --- a/sbr/netsec.c +++ b/sbr/netsec.c @@ -209,12 +209,8 @@ netsec_shutdown(netsec_context *nsc, int closeflag) free(nsc->sasl_hostname); if (nsc->sasl_cbs) free(nsc->sasl_cbs); - if (nsc->sasl_creds) { - if (nsc->sasl_creds->password) - memset(nsc->sasl_creds->password, 0, - strlen(nsc->sasl_creds->password)); - free(nsc->sasl_creds); - } + if (nsc->sasl_creds) + nmh_credentials_free(nsc->sasl_creds); if (nsc->sasl_secret) { if (nsc->sasl_secret->len > 0) { memset(nsc->sasl_secret->data, 0, nsc->sasl_secret->len); @@ -1003,6 +999,13 @@ netsec_set_sasl_params(netsec_context *nsc, const char *hostname, } nsc->sasl_hostname = mh_xstrdup(hostname); + + /* + * Set up our credentials + */ + + nsc->sasl_creds = nmh_get_credentials(nsc->sasl_hostname, nsc->ns_userid); + #else /* CYRUS_SASL */ NMH_UNUSED(hostname); NMH_UNUSED(service); @@ -1043,35 +1046,10 @@ int netsec_get_user(void *context, int id, const char **result, if (! result || (id != SASL_CB_USER && id != SASL_CB_AUTHNAME)) return SASL_BADPARAM; - if (nsc->ns_userid == NULL) { - /* - * Pass the 1 third argument to nmh_get_credentials() so that - * a default user if the -user switch wasn't supplied, and so - * that a default password will be supplied. That's used when - * those values really don't matter, and only with legacy/.netrc, - * i.e., with a credentials profile entry. - */ - - if (nsc->sasl_creds == NULL) { - NEW(nsc->sasl_creds); - nsc->sasl_creds->user = NULL; - nsc->sasl_creds->password = NULL; - } - - if (nmh_get_credentials(nsc->sasl_hostname, nsc->ns_userid, 1, - nsc->sasl_creds) != OK) - return SASL_BADPARAM; + *result = nmh_cred_get_user(nsc->sasl_creds); - if (nsc->ns_userid != nsc->sasl_creds->user) { - if (nsc->ns_userid) - free(nsc->ns_userid); - nsc->ns_userid = getcpy(nsc->sasl_creds->user); - } - } - - *result = nsc->ns_userid; if (len) - *len = strlen(nsc->ns_userid); + *len = strlen(*result); return SASL_OK; } @@ -1085,6 +1063,7 @@ netsec_get_password(sasl_conn_t *conn, void *context, int id, sasl_secret_t **psecret) { netsec_context *nsc = (netsec_context *) context; + const char *password; int len; NMH_UNUSED(conn); @@ -1092,27 +1071,9 @@ netsec_get_password(sasl_conn_t *conn, void *context, int id, if (! psecret || id != SASL_CB_PASS) return SASL_BADPARAM; - if (nsc->sasl_creds == NULL) { - NEW(nsc->sasl_creds); - nsc->sasl_creds->user = NULL; - nsc->sasl_creds->password = NULL; - } - - if (nsc->sasl_creds->password == NULL) { - /* - * Pass the 0 third argument to nmh_get_credentials() so - * that the default password isn't used. With legacy/.netrc - * credentials support, we'll only get here if the -user - * switch to send(1)/post(8) wasn't used. - */ - - if (nmh_get_credentials(nsc->sasl_hostname, nsc->ns_userid, 0, - nsc->sasl_creds) != OK) { - return SASL_BADPARAM; - } - } + password = nmh_cred_get_password(nsc->sasl_creds); - len = strlen(nsc->sasl_creds->password); + len = strlen(password); /* * sasl_secret_t includes 1 bytes for "data" already, so that leaves @@ -1125,7 +1086,7 @@ netsec_get_password(sasl_conn_t *conn, void *context, int id, return SASL_NOMEM; (*psecret)->len = len; - strcpy((char *) (*psecret)->data, nsc->sasl_creds->password); + strcpy((char *) (*psecret)->data, password); nsc->sasl_secret = *psecret; diff --git a/sbr/ruserpass.c b/sbr/ruserpass.c index de0f951b..610f32a5 100644 --- a/sbr/ruserpass.c +++ b/sbr/ruserpass.c @@ -59,7 +59,7 @@ static int token(char *); void -ruserpass(char *host, char **aname, char **apass) +ruserpass(const char *host, char **aname, char **apass, int flags) { int t, usedefault = 0; struct stat stb; @@ -133,7 +133,7 @@ ruserpass(char *host, char **aname, char **apass) } } - if (!*aname) { + if (!*aname && ! (flags & RUSERPASS_NO_PROMPT_USER)) { char tmp[80]; char *myname; @@ -156,7 +156,7 @@ ruserpass(char *host, char **aname, char **apass) *aname = mh_xstrdup(myname); } - if (!*apass) { + if (!*apass && ! (flags & RUSERPASS_NO_PROMPT_PASSWORD)) { char prompt[256]; char *mypass; diff --git a/uip/inc.c b/uip/inc.c index 83b6ec96..aadfe98b 100644 --- a/uip/inc.c +++ b/uip/inc.c @@ -413,25 +413,21 @@ main (int argc, char **argv) * a POP server? */ if (inc_type == INC_POP) { - struct nmh_creds creds = { 0, 0, 0 }; - if (auth_svc == NULL) { if (saslmech && ! strcasecmp(saslmech, "xoauth2")) { adios (NULL, "must specify -authservice with -saslmech xoauth2"); } - nmh_get_credentials (host, user, sasl, &creds); } else { if (user == NULL) { adios (NULL, "must specify -user with -saslmech xoauth2"); } - creds.user = user; } /* * initialize POP connection */ - if (pop_init (host, port, creds.user, creds.password, proxy, snoop, - sasl, saslmech, tls, auth_svc) == NOTOK) + if (pop_init (host, port, user, proxy, snoop, sasl, saslmech, + tls, auth_svc) == NOTOK) adios (NULL, "%s", response); /* Check if there are any messages */ diff --git a/uip/mhparse.c b/uip/mhparse.c index cb7d5652..69ed3bd4 100644 --- a/uip/mhparse.c +++ b/uip/mhparse.c @@ -2515,7 +2515,7 @@ openFTP (CT ct, char **file) LocalName (1)); pass = buffer; } else { - ruserpass (e->eb_site, &username, &password); + ruserpass (e->eb_site, &username, &password, 0); user = username; pass = password; } diff --git a/uip/msgchk.c b/uip/msgchk.c index 0f164c39..a4613c27 100644 --- a/uip/msgchk.c +++ b/uip/msgchk.c @@ -350,23 +350,20 @@ remotemail (char *host, char *port, char *user, char *proxy, int notifysw, const char *auth_svc) { int nmsgs, nbytes, status; - struct nmh_creds creds = { 0, 0, 0 }; if (auth_svc == NULL) { if (saslmech && ! strcasecmp(saslmech, "xoauth2")) { adios (NULL, "must specify -authservice with -saslmech xoauth2"); } - nmh_get_credentials (host, user, sasl, &creds); } else { if (user == NULL) { adios (NULL, "must specify -user with -saslmech xoauth2"); } - creds.user = user; } /* open the POP connection */ - if (pop_init (host, port, creds.user, creds.password, proxy, snoop, sasl, - saslmech, tls, auth_svc) == NOTOK + if (pop_init (host, port, user, proxy, snoop, sasl, saslmech, tls, + auth_svc) == NOTOK || pop_stat (&nmsgs, &nbytes) == NOTOK /* check for messages */ || pop_quit () == NOTOK) { /* quit POP connection */ advise (NULL, "%s", response); diff --git a/uip/popsbr.c b/uip/popsbr.c index cf6b8042..50cdf09b 100644 --- a/uip/popsbr.c +++ b/uip/popsbr.c @@ -18,7 +18,6 @@ #define TRMLEN (sizeof TRM - 1) static int poprint = 0; -static int pophack = 0; char response[BUFSIZ]; static netsec_context *nsc = NULL; @@ -134,8 +133,8 @@ parse_proxy(char *proxy, char *host) } int -pop_init (char *host, char *port, char *user, char *pass, char *proxy, - int snoop, int sasl, char *mech, int tls, const char *oauth_svc) +pop_init (char *host, char *port, char *user, char *proxy, int snoop, + int sasl, char *mech, int tls, const char *oauth_svc) { int fd1, fd2; char buffer[BUFSIZ]; @@ -253,11 +252,21 @@ pop_init (char *host, char *port, char *user, char *pass, char *proxy, return NOTOK; } return OK; - } else - if (command ("USER %s", user) != NOTOK - && command ("%s %s", (pophack++, "PASS"), - pass) != NOTOK) - return OK; + } else { + nmh_creds_t creds; + + if (!(creds = nmh_get_credentials(host, user))) + return NOTOK; + if (command ("USER %s", nmh_cred_get_user(creds)) + != NOTOK) { + if (command("PASS %s", nmh_cred_get_password(creds)) + != NOTOK) { + nmh_credentials_free(creds); + return OK; + } + } + nmh_credentials_free(creds); + } } strncpy (buffer, response, sizeof(buffer)); command ("QUIT");