|
|
|
// 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
|