You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
pam_client/pam_client.c

335 lines
7.7 KiB

// Copyright 2007-2010 Mir Calculate Ltd. http://www.calculate-linux.org
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <security/pam_modules.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <ctype.h>
#include <syslog.h>
#include <ldap.h>
#include <asm/unistd.h>
#define PAM_LDAP_PATH_CONF "/etc/ldap.conf"
#define PASSWD "/etc/passwd"
#define CACHE_PASSWD "/var/lib/calculate/calculate-client/cache/passwd"
#define PAM_SM_AUTH
#define MAX_V 30
#define WAITTIME 30
#define WAITTIMELDAP 60
typedef struct pam_config
{
/* file name read from */
char *configFile;
/* space delimited list of servers */
char *host;
/* port, expected to be common to all servers */
int port;
int version;
/* bind dn/pw for "anonymous" authentication */
char *binddn;
char *bindpw;
} pam_config_t;
static int
_alloc_config (pam_config_t ** presult)
{
pam_config_t *result;
if (*presult == NULL)
{
*presult = (pam_config_t *) calloc (1, sizeof (*result));
if (*presult == NULL)
return PAM_BUF_ERR;
}
result = *presult;
result->configFile = NULL;
result->host = NULL;
result->port = 0;
result->binddn = NULL;
result->bindpw = NULL;
result->version = LDAP_VERSION3;
return PAM_SUCCESS;
}
static int
_read_config (const char *configFile, pam_config_t ** presult)
{
/* this is the same configuration file as nss_ldap */
FILE *fp;
char b[BUFSIZ];
pam_config_t *result;
if (_alloc_config (presult) != PAM_SUCCESS)
{
return PAM_BUF_ERR;
}
result = *presult;
/* configuration file location is configurable; default /etc/ldap.conf */
if (configFile == NULL)
{
configFile = PAM_LDAP_PATH_CONF;
result->configFile = NULL;
}
else
{
result->configFile = strdup (configFile);
if (result->configFile == NULL)
return PAM_BUF_ERR;
}
fp = fopen (configFile, "r");
if (fp == NULL)
{
return PAM_SERVICE_ERR;
}
while (fgets (b, sizeof (b), fp) != NULL)
{
char *k, *v;
int len;
if (*b == '\n' || *b == '#')
continue;
k = b;
v = k;
while (*v != '\0' && *v != ' ' && *v != '\t')
v++;
if (*v == '\0')
continue;
*(v++) = '\0';
/* skip all whitespaces between keyword and value */
while (*v == ' ' || *v == '\t')
v++;
/* kick off all whitespaces and newline at the end of value */
len = strlen (v) - 1;
while (v[len] == ' ' || v[len] == '\t' || v[len] == '\n')
--len;
v[len + 1] = '\0';
if (!strcasecmp (k, "host"))
{
result->host = strdup (v);
}
else if (!strcasecmp (k, "binddn"))
{
result->binddn = strdup (v);
}
else if (!strcasecmp (k, "bindpw"))
{
result->bindpw = strdup (v);
}
else if (!strcasecmp (k, "port"))
{
result->port = atoi (v);
}
else if (!strcasecmp (k, "ldap_version"))
{
result->version = atoi (v);
}
}
if (result->host == NULL || result->binddn == NULL ||
result->bindpw == NULL)
{
/*
* According to PAM Documentation, such an error in a config file
* SHOULD be logged at LOG_ALERT level
*/
syslog (LOG_ALERT, "pam_ldap: missing \"host\" in file \"%s\"",
configFile);
return PAM_SERVICE_ERR;
}
if (result->port == 0)
{
result->port = LDAP_PORT;
}
fclose (fp);
/* can't use _pam_overwrite because it only goes to end of string,
* not the buffer
*/
memset (b, 0, BUFSIZ);
return PAM_SUCCESS;
}
static void
_release_config (pam_config_t ** pconfig)
{
pam_config_t *c;
c = *pconfig;
if (c == NULL)
return;
if (c->configFile != NULL)
free (c->configFile);
if (c->host != NULL)
free (c->host);
if (c->binddn != NULL)
free (c->binddn);
if (c->bindpw != NULL)
free (c->bindpw);
}
static int
_check_ldap (int retry_count)
{
int result = PAM_SERVICE_ERR;
int timelimit = 3;
struct berval userpw;
struct berval *servcred;
char buf[BUFSIZ];
LDAP *ld=NULL;
pam_config_t * config= NULL;
if(_read_config(NULL,&config) != PAM_SUCCESS) {
if(config)
_release_config(&config);
return PAM_SERVICE_ERR;
}
snprintf(buf,BUFSIZ,"ldap://%s:%d",config->host,config->port);
if(ldap_initialize(&ld,buf) == LDAP_SUCCESS &&
ldap_set_option(ld,LDAP_OPT_PROTOCOL_VERSION,
&config->version) == LDAP_SUCCESS &&
ldap_set_option(ld,LDAP_OPT_TIMELIMIT,
&timelimit) == LDAP_SUCCESS) {
userpw.bv_val = config->bindpw;
userpw.bv_len = (userpw.bv_val != 0) ? strlen (userpw.bv_val) : 0;
for(;retry_count;retry_count--) {
if (ldap_sasl_bind_s(ld,config->binddn,LDAP_SASL_SIMPLE,
&userpw, NULL, NULL,&servcred) == LDAP_SUCCESS) {
result = PAM_SUCCESS;
break;
}
else {
result = PAM_TRY_AGAIN;
}
sleep(1);
}
}
if(ld != NULL)
ldap_unbind_ext(ld,NULL,NULL);
_release_config(&config);
return result;
}
int file_exists(const char *fname) {
return access(fname, 0) != -1;
}
// serach user in passwd type file
int search_user(const char *username, char *filename) {
char buf[BUFSIZ];
FILE *fd = fopen(filename,"r");
if(fd != NULL) {
while( fgets(buf,BUFSIZ,fd) ) {
char *tail = buf;
char *token = strsep(&tail,":");
if(token != NULL && strcmp(token,username) == 0) {
fclose(fd);
return 1;
}
}
fclose(fd);
}
return 0;
}
// check: is local user?
int local_user(const char *username) {
int pUser = search_user(username,PASSWD);
int cUser = search_user(username,CACHE_PASSWD);
// local user is user which found in /etc/passwd only
return (pUser && !cUser)?1:0;
}
// Authentication function
PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags
,int argc, const char **argv)
{
int i;
int retval;
char *boot_client="/etc/runlevels/boot/client";
char *default_client="/etc/runlevels/default/client";
char *started_client="/run/openrc/started/client";
char *started_local="/run/openrc/started/local";
char *ldap_conf="/etc/ldap.conf";
const char *login;
// get username
retval = pam_get_user(pamh, &login, "login: ");
// don't wait ldap for authentificate local user
if(retval == PAM_SUCCESS && login && local_user(login))
return PAM_SUCCESS;
// wait for client daemon
if (file_exists(boot_client) || file_exists(default_client)) {
for(i=0;i<WAITTIME;i++) {
if(file_exists(started_client) ||
file_exists(started_local)) {
if(_check_ldap(WAITTIMELDAP-i)!=-1)
return PAM_SUCCESS;
break;
}
sleep(1);
}
}
// break auth if not exists ldap.conf
if (!file_exists(ldap_conf)) {
return PAM_AUTHINFO_UNAVAIL;
}
return PAM_SUCCESS;
}
// Empty function, necessary for pam-module
PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags
,int argc, const char **argv)
{
return PAM_SUCCESS;
}
#ifdef PAM_STATIC
struct pam_module _pam_unix_auth_modstruct = {
"pam_client",
pam_sm_authenticate,
pam_sm_setcred,
NULL,
NULL,
NULL,
NULL,
};
#endif