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