add ability to script IMAP user query
It was already possible to retrieve passwords from arbitrary commands. But this goes only half the way to allowing automated derivation of login credentials, as some environments may also have different user names based on the system. Therefore, add the UserCmd option to complement PassCmd. Based on a patch series by Patrick Steinhardt <ps@pks.im>
This commit is contained in:
parent
503478533c
commit
03b15dbdd3
2
NEWS
2
NEWS
|
@ -11,6 +11,8 @@ Support for configuring a TLS cipher string was added.
|
||||||
|
|
||||||
IMAP mailbox subscriptions are supported now.
|
IMAP mailbox subscriptions are supported now.
|
||||||
|
|
||||||
|
The IMAP user query can be scripted now.
|
||||||
|
|
||||||
[1.3.0]
|
[1.3.0]
|
||||||
|
|
||||||
Network timeout handling has been added.
|
Network timeout handling has been added.
|
||||||
|
|
126
src/drv_imap.c
126
src/drv_imap.c
|
@ -50,6 +50,7 @@ typedef struct imap_server_conf {
|
||||||
char *name;
|
char *name;
|
||||||
server_conf_t sconf;
|
server_conf_t sconf;
|
||||||
char *user;
|
char *user;
|
||||||
|
char *user_cmd;
|
||||||
char *pass;
|
char *pass;
|
||||||
char *pass_cmd;
|
char *pass_cmd;
|
||||||
int max_in_progress;
|
int max_in_progress;
|
||||||
|
@ -1936,12 +1937,50 @@ imap_open_store_authenticate_p3( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
static char *
|
||||||
|
cred_from_cmd( const char *cred, const char *cmd, const char *srv_name )
|
||||||
|
{
|
||||||
|
FILE *fp;
|
||||||
|
int ret;
|
||||||
|
char buffer[8192]; // Hopefully more than enough room for XOAUTH2, etc. tokens
|
||||||
|
|
||||||
|
if (*cmd == '+') {
|
||||||
|
flushn();
|
||||||
|
cmd++;
|
||||||
|
}
|
||||||
|
if (!(fp = popen( cmd, "r" ))) {
|
||||||
|
pipeerr:
|
||||||
|
sys_error( "Skipping account %s, %s failed", srv_name, cred );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!fgets( buffer, sizeof(buffer), fp ))
|
||||||
|
buffer[0] = 0;
|
||||||
|
if ((ret = pclose( fp )) < 0)
|
||||||
|
goto pipeerr;
|
||||||
|
if (ret) {
|
||||||
|
if (WIFSIGNALED( ret ))
|
||||||
|
error( "Skipping account %s, %s crashed\n", srv_name, cred );
|
||||||
|
else
|
||||||
|
error( "Skipping account %s, %s exited with status %d\n", srv_name, cred, WEXITSTATUS( ret ) );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!buffer[0]) {
|
||||||
|
error( "Skipping account %s, %s produced no output\n", srv_name, cred );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
buffer[strcspn( buffer, "\n" )] = 0; /* Strip trailing newline */
|
||||||
|
return nfstrdup( buffer );
|
||||||
|
}
|
||||||
|
|
||||||
static const char *
|
static const char *
|
||||||
ensure_user( imap_server_conf_t *srvc )
|
ensure_user( imap_server_conf_t *srvc )
|
||||||
{
|
{
|
||||||
if (!srvc->user) {
|
if (!srvc->user) {
|
||||||
error( "Skipping account %s, no user\n", srvc->name );
|
if (srvc->user_cmd) {
|
||||||
return NULL;
|
srvc->user = cred_from_cmd( "UserCmd", srvc->user_cmd, srvc->name );
|
||||||
|
} else {
|
||||||
|
error( "Skipping account %s, no user\n", srvc->name );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return srvc->user;
|
return srvc->user;
|
||||||
}
|
}
|
||||||
|
@ -1949,56 +1988,25 @@ ensure_user( imap_server_conf_t *srvc )
|
||||||
static const char *
|
static const char *
|
||||||
ensure_password( imap_server_conf_t *srvc )
|
ensure_password( imap_server_conf_t *srvc )
|
||||||
{
|
{
|
||||||
char *cmd = srvc->pass_cmd;
|
if (!srvc->pass) {
|
||||||
|
if (srvc->pass_cmd) {
|
||||||
if (cmd) {
|
srvc->pass = cred_from_cmd( "PassCmd", srvc->pass_cmd, srvc->name );
|
||||||
FILE *fp;
|
} else {
|
||||||
int ret;
|
|
||||||
char buffer[8192]; // Hopefully more than enough room for XOAUTH2, etc. tokens
|
|
||||||
|
|
||||||
if (*cmd == '+') {
|
|
||||||
flushn();
|
flushn();
|
||||||
cmd++;
|
char prompt[80];
|
||||||
|
sprintf( prompt, "Password (%s): ", srvc->name );
|
||||||
|
char *pass = getpass( prompt );
|
||||||
|
if (!pass) {
|
||||||
|
perror( "getpass" );
|
||||||
|
exit( 1 );
|
||||||
|
}
|
||||||
|
if (!*pass) {
|
||||||
|
error( "Skipping account %s, no password\n", srvc->name );
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
/* getpass() returns a pointer to a static buffer. Make a copy for long term storage. */
|
||||||
|
srvc->pass = nfstrdup( pass );
|
||||||
}
|
}
|
||||||
if (!(fp = popen( cmd, "r" ))) {
|
|
||||||
pipeerr:
|
|
||||||
sys_error( "Skipping account %s, password command failed", srvc->name );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!fgets( buffer, sizeof(buffer), fp ))
|
|
||||||
buffer[0] = 0;
|
|
||||||
if ((ret = pclose( fp )) < 0)
|
|
||||||
goto pipeerr;
|
|
||||||
if (ret) {
|
|
||||||
if (WIFSIGNALED( ret ))
|
|
||||||
error( "Skipping account %s, password command crashed\n", srvc->name );
|
|
||||||
else
|
|
||||||
error( "Skipping account %s, password command exited with status %d\n", srvc->name, WEXITSTATUS( ret ) );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
if (!buffer[0]) {
|
|
||||||
error( "Skipping account %s, password command produced no output\n", srvc->name );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
buffer[strcspn( buffer, "\n" )] = 0; /* Strip trailing newline */
|
|
||||||
free( srvc->pass ); /* From previous runs */
|
|
||||||
srvc->pass = nfstrdup( buffer );
|
|
||||||
} else if (!srvc->pass) {
|
|
||||||
char *pass, prompt[80];
|
|
||||||
|
|
||||||
flushn();
|
|
||||||
sprintf( prompt, "Password (%s): ", srvc->name );
|
|
||||||
pass = getpass( prompt );
|
|
||||||
if (!pass) {
|
|
||||||
perror( "getpass" );
|
|
||||||
exit( 1 );
|
|
||||||
}
|
|
||||||
if (!*pass) {
|
|
||||||
error( "Skipping account %s, no password\n", srvc->name );
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
/* getpass() returns a pointer to a static buffer. Make a copy for long term storage. */
|
|
||||||
srvc->pass = nfstrdup( pass );
|
|
||||||
}
|
}
|
||||||
return srvc->pass;
|
return srvc->pass;
|
||||||
}
|
}
|
||||||
|
@ -2182,6 +2190,17 @@ imap_open_store_authenticate2( imap_store_t *ctx )
|
||||||
char saslmechs[1024], *saslend = saslmechs;
|
char saslmechs[1024], *saslend = saslmechs;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Ensure that there are no leftovers from previous runs. This is needed in case
|
||||||
|
// the credentials have a timing dependency or otherwise lose validity after use.
|
||||||
|
if (srvc->user_cmd) {
|
||||||
|
free( srvc->user );
|
||||||
|
srvc->user = NULL;
|
||||||
|
}
|
||||||
|
if (srvc->pass_cmd) {
|
||||||
|
free( srvc->pass );
|
||||||
|
srvc->pass = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
info( "Logging in...\n" );
|
info( "Logging in...\n" );
|
||||||
for (mech = srvc->auth_mechs; mech; mech = mech->next) {
|
for (mech = srvc->auth_mechs; mech; mech = mech->next) {
|
||||||
int any = !strcmp( mech->string, "*" );
|
int any = !strcmp( mech->string, "*" );
|
||||||
|
@ -3268,6 +3287,8 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
|
||||||
}
|
}
|
||||||
else if (!strcasecmp( "User", cfg->cmd ))
|
else if (!strcasecmp( "User", cfg->cmd ))
|
||||||
server->user = nfstrdup( cfg->val );
|
server->user = nfstrdup( cfg->val );
|
||||||
|
else if (!strcasecmp( "UserCmd", cfg->cmd ))
|
||||||
|
server->user_cmd = nfstrdup( cfg->val );
|
||||||
else if (!strcasecmp( "Pass", cfg->cmd ))
|
else if (!strcasecmp( "Pass", cfg->cmd ))
|
||||||
server->pass = nfstrdup( cfg->val );
|
server->pass = nfstrdup( cfg->val );
|
||||||
else if (!strcasecmp( "PassCmd", cfg->cmd ))
|
else if (!strcasecmp( "PassCmd", cfg->cmd ))
|
||||||
|
@ -3431,6 +3452,11 @@ imap_parse_store( conffile_t *cfg, store_conf_t **storep )
|
||||||
cfg->err = 1;
|
cfg->err = 1;
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
if (server->user && server->user_cmd) {
|
||||||
|
error( "%s '%s' has both User and UserCmd\n", type, name );
|
||||||
|
cfg->err = 1;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
if (server->pass && server->pass_cmd) {
|
if (server->pass && server->pass_cmd) {
|
||||||
error( "%s '%s' has both Pass and PassCmd\n", type, name );
|
error( "%s '%s' has both Pass and PassCmd\n", type, name );
|
||||||
cfg->err = 1;
|
cfg->err = 1;
|
||||||
|
|
|
@ -315,6 +315,15 @@ Zero means unlimited.
|
||||||
Specify the login name on the IMAP server.
|
Specify the login name on the IMAP server.
|
||||||
.
|
.
|
||||||
.TP
|
.TP
|
||||||
|
\fBUserCmd\fR [\fB+\fR]\fIcommand\fR
|
||||||
|
Specify a shell command to obtain a user rather than specifying a
|
||||||
|
user directly. This allows you to script retrieving user names.
|
||||||
|
The command must produce exactly one line on stdout; the trailing newline
|
||||||
|
is optional.
|
||||||
|
Prepend \fB+\fR to the command to indicate that it produces TTY output
|
||||||
|
(e.g., a prompt); failure to do so will merely produce messier output.
|
||||||
|
.
|
||||||
|
.TP
|
||||||
\fBPass\fR \fIpassword\fR
|
\fBPass\fR \fIpassword\fR
|
||||||
Specify the password for \fIusername\fR on the IMAP server.
|
Specify the password for \fIusername\fR on the IMAP server.
|
||||||
Note that this option is \fInot\fR required.
|
Note that this option is \fInot\fR required.
|
||||||
|
|
Loading…
Reference in New Issue
Block a user