/*
* authswitch.c:
* authentication driver switch
*
* Copyright (c) 2001 Chris Lightfoot. All rights reserved.
*
*/
static const char rcsid[] = "$Id: authswitch.c,v 1.42 2003/11/24 19:58:28 chris Exp $";
#ifdef HAVE_CONFIG_H
#include "configuration.h"
#endif /* HAVE_CONFIG_H */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>
#include <sys/types.h>
#ifdef AUTH_LDAP
#include "auth_ldap.h"
#endif /* AUTH_LDAP */
#ifdef AUTH_MYSQL
#include "auth_mysql.h"
#endif /* AUTH_MYSQL */
#ifdef AUTH_PGSQL
#include "auth_pgsql.h"
#endif /* AUTH_PGSQL */
#ifdef AUTH_OTHER
#include "auth_other.h"
#endif /* AUTH_OTHER */
#ifdef AUTH_FLATFILE
#include "auth_flatfile.h"
#endif /* AUTH_FLATFILE */
#ifdef AUTH_PERL
#include "auth_perl.h"
#endif /* AUTH_PERL */
#ifdef AUTH_PAM
#include "auth_pam.h"
#endif /* AUTH_PAM */
#ifdef AUTH_PASSWD
#include "auth_passwd.h"
#endif /* AUTH_PASSWD */
#ifdef USE_WHOSON
#include <whoson.h>
#endif
#include "authswitch.h"
#include "config.h"
#include "stringmap.h"
#include "util.h"
/* auth_drivers:
* References the various authentication drivers. New ones should be added as
* below. */
#define _X(String) (String)
struct authdrv auth_drivers[] = {
#ifdef AUTH_PAM
/* This is the PAM driver, which should be used wherever possible. */
{NULL, NULL, auth_pam_new_user_pass, NULL, NULL, NULL,
"pam",
_X("Uses Pluggable Authentication Modules")},
#endif /* AUTH_PAM */
#ifdef AUTH_PASSWD
/* This is the old-style unix authentication driver. */
{NULL, NULL, auth_passwd_new_user_pass, NULL, NULL, NULL,
"passwd",
_X("Uses /etc/passwd or /etc/shadow")},
#endif /* AUTH_PASSWD */
#ifdef AUTH_MYSQL
/* This is for vmail-sql and similar schemes. */
{auth_mysql_init, auth_mysql_new_apop, auth_mysql_new_user_pass, auth_mysql_onlogin, auth_mysql_postfork, auth_mysql_close,
"mysql",
_X("Uses a MySQL database")},
#endif /* AUTH_MYSQL */
#ifdef AUTH_PGSQL
/* Postgres. */
{auth_pgsql_init, auth_pgsql_new_apop, auth_pgsql_new_user_pass, auth_pgsql_onlogin, auth_pgsql_postfork, auth_pgsql_close,
"pgsql",
_X("Uses a Postgres database")},
#endif /* AUTH_PGSQL */
#ifdef AUTH_LDAP
/* Authenticate against a directory. */
{auth_ldap_init, NULL, auth_ldap_new_user_pass, NULL, auth_ldap_postfork, auth_ldap_close,
"ldap",
_X("Uses an LDAP directory")},
#endif /* AUTH_LDAP */
#ifdef AUTH_OTHER
/* This talks to an external program. */
{auth_other_init, auth_other_new_apop, auth_other_new_user_pass, auth_other_onlogin, auth_other_postfork, auth_other_close,
"other",
_X("Uses an external program")},
#endif /* AUTH_OTHER */
#ifdef AUTH_PERL
/* This calls into perl subroutines. */
{auth_perl_init, auth_perl_new_apop, auth_perl_new_user_pass, auth_perl_onlogin, auth_perl_postfork, auth_perl_close,
"perl",
_X("Uses perl code")},
#endif /* AUTH_PERL */
#ifdef AUTH_FLATFILE
/* Authenticate against /etc/passwd-style flat files. */
{auth_flatfile_init, auth_flatfile_new_apop, auth_flatfile_new_user_pass, NULL, NULL, NULL,
"flatfile",
_X("Uses /etc/passwd-style flat files")},
#endif /* AUTH_FLATFILE */
};
int *auth_drivers_running;
#define NUM_AUTH_DRIVERS (sizeof(auth_drivers) / sizeof(struct authdrv))
#define auth_drivers_end auth_drivers + NUM_AUTH_DRIVERS
/* username_string:
* Return a string describing the name of a user, of the form
* [<user>; <local-part>@<domain>] */
char *username_string(const char *user, const char *local_part, const char *domain) {
static char *buf;
static size_t nbuf;
size_t l;
if (local_part && domain) {
if (nbuf < (l = strlen(user) + strlen(local_part) + strlen(domain) + 6))
buf = xrealloc(buf, nbuf = l);
sprintf(buf, "[%s; %s@%s]", user, local_part, domain);
} else if (domain) {
if (nbuf < (l = strlen(user) + strlen(domain) + 6))
buf = xrealloc(buf, nbuf = l);
sprintf(buf, "[%s; @%s]", user, domain);
} else {
if (nbuf < (l = strlen(user) + 3))
buf = xrealloc(buf, nbuf = l);
sprintf(buf, "[%s]", user);
}
return buf;
}
/* authswitch_describe:
* Describe available authentication drivers. */
void authswitch_describe(FILE *fp) {
const struct authdrv *aa;
fprintf(fp, _("Available authentication drivers:\n\n"));
for (aa = auth_drivers; aa < auth_drivers_end; ++aa)
fprintf(fp, " auth-%-11s %s\n", aa->name, _(aa->description));
fprintf(fp, "\n");
}
/* authswitch_init:
* Attempt to initialise all the authentication drivers listed in
* auth_drivers. Returns the number of drivers successfully started. */
extern stringmap config;
#ifdef USE_DRAC
static char *drac_server;
#endif
#ifdef USE_WHOSON
static int whoson_enable;
#endif
int authswitch_init(void) {
const struct authdrv *aa;
int *aar;
int ret = 0;
auth_drivers_running = xcalloc(NUM_AUTH_DRIVERS, sizeof *auth_drivers_running);
for (aa = auth_drivers, aar = auth_drivers_running; aa < auth_drivers_end; ++aa, ++aar) {
size_t l;
char *s;
s = xmalloc(l = (13 + strlen(aa->name)));
snprintf(s, l, "auth-%s-enable", aa->name);
if (config_get_bool(s)) {
if (aa->auth_init && !aa->auth_init())
log_print(LOG_ERR, _("failed to initialise %s authentication driver"), aa->name);
else {
*aar = 1;
++ret;
}
}
xfree(s);
}
#ifdef USE_DRAC
if ((drac_server = config_get_string("drac-server")))
log_print(LOG_INFO, _("will notify DRAC server `%s' of logins"), drac_server);
#endif
#ifdef USE_WHOSON
if ((whoson_enable = config_get_bool("whoson-enable")))
log_print(LOG_INFO, _("will notify logins by WHOSON"));
#endif
return ret;
}
/* authcontext_new_apop:
* Attempts to authenticate the apop data with each driver in turn. */
authcontext authcontext_new_apop(const char *user, const char *local_part, const char *domain, const char *timestamp, const unsigned char *digest, const char *clienthost, const char *serverhost) {
authcontext a = NULL;
const struct authdrv *aa;
int *aar;
char *x = NULL;
const char *l = NULL, *d = NULL;
l = local_part;
d = domain;
/* If no local-part has been explicitly supplied, then we try to construct
* one by splitting up the username over one of the characters listed in
* DOMAIN_SEPARATORS. This is distinct from the append-domain
* functionality, which will attempt to use the user's supplied username
* as a local-part, with the listener domain as the domain, and the
* strip-domain functionality, which will suppress the domain supplied by
* the user. */
if (!local_part && domain) {
int n;
n = strcspn(user, DOMAIN_SEPARATORS);
if (n > 0 && user[n]) {
x = xstrdup(user);
x[n] = 0;
l = x;
d = l + n + 1;
} else
l = NULL;
}
for (aa = auth_drivers, aar = auth_drivers_running; aa < auth_drivers_end; ++aa, ++aar)
if (*aar && aa->auth_new_apop && (a = aa->auth_new_apop(user, l, d, timestamp, digest, clienthost, serverhost))) {
a->auth = xstrdup(aa->name);
a->user = xstrdup(user);
if (!a->local_part && l)
a->local_part = xstrdup(l);
if (!a->domain && d)
a->domain = xstrdup(d);
log_print(LOG_INFO, _("authcontext_new_apop: began session for `%s' with %s; uid %d, gid %d"), a->user, a->auth, a->uid, a->gid);
break;
}
xfree(x);
return a;
}
/* authcontext_new_user_pass:
* Attempts to authenticate user and pass with each driver in turn. */
authcontext authcontext_new_user_pass(const char *user, const char *local_part, const char *domain, const char *pass, const char *clienthost, const char *serverhost) {
authcontext a = NULL;
const struct authdrv *aa;
int *aar;
char *x = NULL;
const char *l = NULL, *d = NULL;
/* This is here mainly for users who forgot to switch off LDAP anonymous
* authentication.... */
if (*pass == 0 && !config_get_bool("permit-empty-password")) {
log_print(LOG_WARNING, _("authcontext_new_user_pass: rejecting login attempt by `%s' with empty password"), user);
return NULL;
}
l = local_part;
d = domain;
/* Maybe split local part and domain (see above). */
if (!local_part && domain) {
int n;
n = strcspn(user, DOMAIN_SEPARATORS);
if (n > 0 && user[n]) {
x = xstrdup(user);
x[n] = 0;
l = x;
d = l + n + 1;
} else
l = NULL;
}
if ((a = authcache_new_user_pass(user, l, d, pass, clienthost, serverhost)))
return a;
for (aa = auth_drivers, aar = auth_drivers_running; aa < auth_drivers_end; ++aa, ++aar)
if (*aar && aa->auth_new_user_pass && (a = aa->auth_new_user_pass(user, l, d, pass, clienthost, serverhost))) {
a->auth = xstrdup(aa->name);
a->user = xstrdup(user);
if (!a->local_part) {
if (l)
a->local_part = xstrdup(l);
else
a->local_part = xstrdup(user);
}
if (!a->domain && d)
a->domain = xstrdup(d);
authcache_save(a, user, l, d, pass, clienthost, serverhost);
log_print(LOG_INFO, _("authcontext_new_user_pass: began session for `%s' with %s; uid %d, gid %d"), a->user, a->auth, a->uid, a->gid);
break;
}
xfree(x);
return a;
}
/* authswitch_onlogin:
* Pass news of a successful login to any authentication drivers which are
* interested in hearing about it. host is the IP address in dotted-quad
* form. */
void authswitch_onlogin(const authcontext A, const char *clienthost, const char *serverhost) {
const struct authdrv *aa;
int *aar;
#ifdef USE_DRAC
/* in -ldrac */
int dracauth(char *server, unsigned long userip, char **errmsg);
#endif
#ifdef USE_WHOSON
char buf[128] = {0};
/* Notify whoson server the user has logged in correctly */
if (wso_login(clienthost, A->user, buf, sizeof(buf)) == -1)
log_print(LOG_ERR, "authswitch_onlogin: wso_login: %s", buf);
#endif /* USE_WHOSON */
#ifdef USE_DRAC
/* Optionally, notify a DRAC -- dynamic relay authentication control --
* server of the login. This uses some wacky RPC thing contained in
* -ldrac. */
if (drac_server) {
char *errmsg;
if (dracauth(drac_server, inet_addr(clienthost), &errmsg))
log_print(LOG_ERR, "authswitch_onlogin: dracauth: %s", errmsg);
}
#endif /* USE_DRAC */
for (aa = auth_drivers, aar = auth_drivers_running; aa < auth_drivers_end; ++aa, ++aar)
if (*aar && aa->auth_onlogin)
aa->auth_onlogin(A, clienthost, serverhost);
}
/* authswitch_postfork:
* Do post-fork cleanup if defined by each driver. */
void authswitch_postfork() {
const struct authdrv *aa;
int *aar;
for (aa = auth_drivers, aar = auth_drivers_running; aa < auth_drivers_end; ++aa, ++aar)
if (*aar && aa->auth_postfork) aa->auth_postfork();
}
/* authswitch_close:
* Closes down any authentication drivers which have been started. */
void authswitch_close() {
const struct authdrv *aa;
int *aar;
for (aa = auth_drivers, aar = auth_drivers_running; aa < auth_drivers_end; ++aa, ++aar)
if (*aar && aa->auth_close) aa->auth_close();
xfree(auth_drivers_running);
}
/* authcontext_new:
* Fill in a new authentication context structure with the given information. */
authcontext authcontext_new(const uid_t uid, const gid_t gid, const char *mboxdrv, const char *mailbox, const char *home) {
authcontext a;
alloc_struct(_authcontext, a);
a->uid = uid;
a->gid = gid;
if (mboxdrv)
a->mboxdrv = xstrdup(mboxdrv);
if (mailbox)
a->mailbox = xstrdup(mailbox);
a->auth = NULL;
a->user = NULL;
if (home)
a->home = xstrdup(home);
return a;
}
/* authcontext_delete:
* Free data associated with an authentication context. */
extern int post_fork; /* in main.c */
void authcontext_delete(authcontext a) {
if (!a) return;
xfree(a->mboxdrv);
xfree(a->mailbox);
xfree(a->auth);
xfree(a->user);
xfree(a->local_part);
xfree(a->domain);
xfree(a->home);
xfree(a);
}
syntax highlighted by Code2HTML, v. 0.9.1