db-checkpassword.c revision 290ad6b6b5fd61a61a4c8f7f6a6c18ff7ac344cf
/* Copyright (c) 2004-2012 Dovecot authors, see the included COPYING file */
#include "auth-common.h"
#if defined(PASSDB_CHECKPASSWORD) || defined(USERDB_CHECKPASSWORD)
#include "lib-signals.h"
#include "buffer.h"
#include "str.h"
#include "ioloop.h"
#include "hash.h"
#include "execv-const.h"
#include "env-util.h"
#include "safe-memset.h"
#include "strescape.h"
#include "child-wait.h"
#include "var-expand.h"
#include "db-checkpassword.h"
#include <stdlib.h>
#include <unistd.h>
#define CHECKPASSWORD_MAX_REQUEST_LEN 512
struct chkpw_auth_request {
struct db_checkpassword *db;
struct auth_request *request;
char *auth_password;
void *context;
unsigned int output_pos, output_len;
int exit_status;
unsigned int exited:1;
};
struct db_checkpassword {
struct hash_table *clients;
struct child_wait *child_wait;
};
static void env_put_extra_fields(const char *extra_fields)
{
const char *const *tmp;
const char *key, *p;
if (p == NULL)
else
}
}
{
i_error("checkpassword: close() failed: %m");
}
i_error("checkpassword: close() failed: %m");
}
}
{
}
}
}
{
const char *const *extra_fields;
}
{
}
static void
{
switch (request->exit_status) {
/* vpopmail exit codes: */
case 3: /* password fail / vpopmail user not found */
case 12: /* null user name given */
case 13: /* null password given */
case 15: /* user has no password */
case 21: /* system user not found */
case 22: /* system user shadow entry not found */
case 23: /* system password fail */
/* standard checkpassword exit codes: */
case 1:
/* (1 is additionally defined in vpopmail for
"Login failed (status=%d)",
break;
case 0:
"Received no input");
break;
}
break;
case 2:
/* checkpassword is called with wrong parameters? unlikely */
"Child %s exited with status 2 (tried to use "
"userdb-only checkpassword program for passdb?)",
break;
case 111:
/* temporary problem, treat as internal error */
default:
/* whatever error.. */
"Child %s exited with status %d",
break;
}
}
static void
{
switch (request->exit_status) {
case 3:
/* User does not exist. */
"User unknown");
break;
case 2:
/* This is intentionally not 0. checkpassword-reply exits with
2 on success when AUTHORIZED is set. */
"Received no input");
break;
}
break;
default:
/* whatever error... */
"Child %s exited with status %d",
break;
}
}
static void
{
/* the process must have exited, and the input fd must have closed */
return;
else
}
{
const struct var_expand_table *tab;
unsigned int i;
}
}
}
{
/* Besides passing the standard username and password in a
pipe, also pass some other possibly interesting information
/* FIXME: for backwards compatibility only,
remove some day */
}
/* FIXME: for backwards compatibility only,
remove some day */
}
if (request->local_port != 0) {
request->local_port));
}
if (request->remote_port != 0) {
request->remote_port));
}
}
const char *fields =
/* extra fields could come from master db */
}
}
static const char *
const char *checkpassword_reply_path)
{
}
{
unsigned char buf[1024];
if (ret > 0) {
return;
}
if (ret < 0) {
"checkpassword", "read() failed: %m");
"LF characters in checkpassword reply");
} else {
}
}
{
/* Send: username \0 password \0 timestamp \0.
Must be 512 bytes or less. The "timestamp" parameter is actually
useful only for APOP authentication. We don't support it, so
keep it empty */
const unsigned char *data;
} else {
}
/* already checked this */
if (ret <= 0) {
if (ret < 0) {
"checkpassword", "write() failed: %m");
} else {
"checkpassword", "write() returned 0");
}
return;
}
return;
/* finished sending the data */
i_error("checkpassword: close() failed: %m");
}
static void ATTR_NORETURN
{
/* fd 3 is used to send the username+password for the script
fd 4 is used to communicate with checkpassword-reply */
"dup2() failed: %m");
exit(111);
}
if (!authenticate) {
authorization, so we need to signalize the
checkpassword program that the password shall be
ignored by setting AUTHORIZED. This needs a
special checkpassword program which knows how to
handle this. */
env_put("AUTHORIZED=1");
/* passdb credentials lookup */
env_put("CREDENTIALS_LOOKUP=1");
}
}
/* very simple argument splitting. */
}
struct db_checkpassword *db)
{
struct chkpw_auth_request *request =
"Child %s died with signal %d",
"checkpassword", "exit_status=%d",
} else {
/* shouldn't happen */
"Child %s exited with status=%d",
}
}
struct auth_request *request,
const char *auth_password,
void *context)
{
struct chkpw_auth_request *chkpw_auth_request;
unsigned int output_len;
/* <username> \0 <password> \0 timestamp \0 */
if (auth_password != NULL)
if (output_len > CHECKPASSWORD_MAX_REQUEST_LEN) {
"Username+password combination too long (%u bytes)",
return;
}
fd_in[0] = -1;
"pipe() failed: %m");
if (fd_in[0] != -1) {
}
return;
}
if (pid == -1) {
"fork() failed: %m");
return;
}
if (pid == 0) {
/* child */
auth_password != NULL);
/* not reached */
}
"close(fd_in[1]) failed: %m");
}
"close(fd_out[0]) failed: %m");
}
}
struct db_checkpassword *
db_checkpassword_init(const char *checkpassword_path,
const char *checkpassword_reply_path)
{
struct db_checkpassword *db;
db->child_wait =
return db;
}
{
struct hash_iterate_context *iter;
}
}
#endif