pam_nufw.c

Go to the documentation of this file.
00001 /*
00002  ** Copyright 2006 - INL
00003  ** Written by Jean Gillaux <jean@inl.fr>
00004  ** INL http://www.inl.fr/
00005  ** Based on pam_permit by Andrew Morgan <morgan@parc.power.net> 1996/3/11
00006  **
00007  ** $Id: pam_nufw.c 4847 2008-06-13 14:48:31Z regit $
00008  **
00009  ** This program is free software; you can redistribute it and/or modify
00010  ** it under the terms of the GNU General Public License as published by
00011  ** the Free Software Foundation, version 3 of the License.
00012  **
00013  ** This program is distributed in the hope that it will be useful,
00014  ** but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  ** GNU General Public License for more details.
00017  **
00018  ** You should have received a copy of the GNU General Public License
00019  ** along with this program; if not, write to the Free Software
00020  ** Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00021  */
00022 
00023 
00024 #define _GNU_SOURCE
00025 #include "../lib/nuclient.h"
00026 #include <sys/resource.h>       /* setrlimit() */
00027 #include <stdio.h>
00028 #include <locale.h>
00029 #include <langinfo.h>
00030 #include <syslog.h>
00031 #include <pwd.h>
00032 #include <signal.h>
00033 #include <stdlib.h>
00034 #include "security.h"
00035 
00036 /*
00037  * here, we make definitions for the externally accessible functions
00038  * in this file (these definitions are required for static modules
00039  * but strongly encouraged generally) they are used to instruct the
00040  * modules include file to define their prototypes.
00041  */
00042 
00043 #define PAM_SM_AUTH
00044 #define PAM_SM_ACCOUNT
00045 #define PAM_SM_SESSION
00046 #define PAM_SM_PASSWORD
00047 
00048 #include <security/pam_modules.h>
00049 #include <security/_pam_macros.h>
00050 
00051 
00052 #define NUAUTH_SRV "192.168.12.1"
00053 #define NUAUTH_PORT "4129"
00054 #define FILE_LOCK ".pam_nufw"
00055 
00056 #define MAX_RETRY_TIME 30
00057 
00058 #define MAX_NOAUTH_USERS 10
00059 
00060 const char *DEFAULT_USER = "nobody";
00061 
00062 /*int noauth_cpt = 0;*/
00063 char **no_auth_users = NULL;
00064 struct pam_nufw_s pn_s;
00065 nuauth_session_t *session = NULL;
00066 char *locale_charset = NULL;
00067 
00068 /* internal data */
00069 struct pam_nufw_s {
00070         char nuauth_srv[BUFSIZ];        /* auth server to connect to */
00071         char nuauth_port[20];   /* port to use on auth server */
00072         char file_lock[BUFSIZ]; /* file lock used to store pid */
00073         char **no_auth_users;
00074         int no_auth_cpt;
00075         nuclient_error_t *err;
00076 };
00077 
00078 /* init pam_nufw info struct. returns error message, or NULL if no error occurs */
00079 static char *_init_pam_nufw_s(struct pam_nufw_s *pn_s)
00080 {
00081         struct rlimit core_limit;
00082 
00083         /* Avoid creation of core file which may contains username and password */
00084         if (getrlimit(RLIMIT_CORE, &core_limit) == 0) {
00085                 core_limit.rlim_cur = 0;
00086                 setrlimit(RLIMIT_CORE, &core_limit);
00087         }
00088 
00089         /* Setup locale */
00090         setlocale(LC_ALL, "");
00091 
00092         /* get local charset */
00093         locale_charset = nl_langinfo(CODESET);
00094         if (locale_charset == NULL) {
00095                 return "Can't get locale charset!";
00096         }
00097 
00098         /* Move to root directory to not block current working directory */
00099         (void) chdir("/");
00100 
00101         memset(pn_s, 0, sizeof(*pn_s));
00102         SECURE_STRNCPY(pn_s->nuauth_srv, NUAUTH_SRV,
00103                        sizeof(pn_s->nuauth_srv));
00104         SECURE_STRNCPY(pn_s->nuauth_port, NUAUTH_PORT,
00105                        sizeof(pn_s->nuauth_port));
00106         SECURE_STRNCPY(pn_s->file_lock, FILE_LOCK,
00107                        sizeof(pn_s->file_lock));
00108         pn_s->no_auth_users = NULL;
00109         pn_s->no_auth_cpt = 0;
00110         return NULL;
00111 }
00112 
00113 /*  function to parse arguments */
00114 static int _pam_parse(int argc, const char **argv, struct pam_nufw_s *pn)
00115 {
00116         int ctrl = 0;
00117         char *noauth;
00118         char *user;
00119         char *search = ",";
00120         int noauth_cpt = 0;
00121         char **no_auth_users = malloc(sizeof(char *) * MAX_NOAUTH_USERS);
00122         for (ctrl = 0; argc-- > 0; ++argv) {
00123                 if (!strncmp(*argv, "server=", 7)) {
00124                         SECURE_STRNCPY(pn->nuauth_srv, *argv + 7,
00125                                        sizeof(pn->nuauth_srv));
00126                 } else if (!strncmp(*argv, "port=", 5)) {
00127                         SECURE_STRNCPY(pn->nuauth_port, *argv + 5,
00128                                        sizeof(pn->nuauth_port));
00129                 } else if (!strncmp(*argv, "lock=", 5)) {
00130                         SECURE_STRNCPY(pn->file_lock, *argv + 5,
00131                                        sizeof(pn->file_lock));
00132                 } else if (!strncmp(*argv, "noauth=", 7)) {
00133                         noauth = strdup(*argv + 7);
00134                         user = strtok(noauth, search);
00135                         if (user) {
00136                                 no_auth_users[noauth_cpt] = x_strdup(user);
00137                                 noauth_cpt++;
00138                         }
00139                         while ((user = strtok(NULL, search)) != NULL) {
00140                                 no_auth_users[noauth_cpt] = x_strdup(user);
00141                                 noauth_cpt++;
00142                         }
00143                 }
00144         }
00145         pn->no_auth_cpt = noauth_cpt;
00146         pn->no_auth_users = no_auth_users;
00147         return ctrl;
00148 }
00149 
00150 char *_get_runpid(struct pam_nufw_s *pn_s, char *home)
00151 {
00152         char path_dir[1024];
00153         int free_home = 0;
00154         if (home == NULL) {
00155                 home = nu_get_home_dir();
00156                 free_home = 1;
00157         }
00158         if (home == NULL) {
00159                 return NULL;
00160         }
00161 
00162         /* create directory path */
00163         snprintf(path_dir, sizeof(path_dir), "%s/.nufw", home);
00164         path_dir[sizeof(path_dir) - 1] = 0;
00165 
00166         /* if the directory doesn't exist, create it */
00167         if (access(path_dir, R_OK)) {
00168                 mkdir(path_dir, S_IRWXU);
00169         }
00170 
00171         /* create pid file full path */
00172         snprintf(path_dir, sizeof(path_dir), "%s/.nufw/%s", home,
00173                  pn_s->file_lock);
00174         path_dir[sizeof(path_dir) - 1] = 0;
00175         if (free_home) {
00176                 free(home);
00177         }
00178         return (char *) strdup(path_dir);
00179 }
00180 
00181 static int _kill_nuclient(char *runpid)
00182 {
00183         pid_t pid;
00184         FILE *FD;
00185         int ok, ret;
00186 
00187         if (runpid) {
00188                 FD = fopen(runpid, "r");
00189                 if (FD) {
00190                         fscanf(FD, "%d", &pid);
00191                         fclose(FD);
00192                         ret = kill(pid, SIGTERM);
00193                         ok = (ret == 0);
00194                         if (ok) {
00195                                 syslog(LOG_INFO,
00196                                        "(pam_nufw) process killed (pid %lu)\n",
00197                                        (unsigned long) pid);
00198                                 return 0;
00199                         } else {
00200                                 syslog(LOG_ERR,
00201                                        "(pam_nufw) fail to kill process: remove pid file\n");
00202                                 unlink(runpid);
00203                                 return 1;
00204                         }
00205                 }
00206                 free(runpid);
00207         }
00208         return 0;
00209 }
00210 
00211 /* function used to
00212  * kill client
00213  * free nuauth session and nuerror
00214  */
00215 void exit_client()
00216 {
00217         char *runpid;
00218         if (session) {
00219                 nu_client_delete(session);
00220         }
00221         runpid = _get_runpid(&pn_s, NULL);
00222         if (runpid != NULL) {
00223                 unlink(runpid);
00224                 free(runpid);
00225         }
00226         nu_client_global_deinit();
00227         nu_client_error_destroy(pn_s.err);
00228         exit(EXIT_SUCCESS);
00229 }
00230 
00231 /* test if username is on the list of users that  don't have to be authenticated */
00232 int do_auth_on_user(const char *username)
00233 {
00234         int i;
00235         for (i = 0; i < pn_s.no_auth_cpt; i++) {
00236                 if (strcmp(pn_s.no_auth_users[i], username) == 0) {
00237                         return 1;
00238                 }
00239         }
00240         return 0;
00241 }
00242 
00243 
00244 /* --- authentication management functions --- */
00245 
00251 nuauth_session_t *do_connect(char *username, char *password, nuclient_error_t * err)
00252 {
00253         nuauth_session_t *session = nu_client_new(username, password, 1, err);
00254         if (session == NULL) {
00255                 return NULL;
00256         }
00257 
00258         /* wipe out username and password, and then freee memory */
00259         memset(username, 0, strlen(username));
00260         memset(password, 0, strlen(password));
00261         free(username);
00262         free(password);
00263 
00264 #if 0
00265         nu_client_set_debug(session, context->debug_mode);
00266 
00267         if (!nu_client_setup_tls(session, NULL, NULL, NULL, NULL, err)) {
00268                 nu_client_delete(session);
00269                 return NULL;
00270         }
00271 #endif
00272 
00273         if (!nu_client_connect
00274             (session, pn_s.nuauth_srv, pn_s.nuauth_port, err)) {
00275                 nu_client_delete(session);
00276                 return NULL;
00277         }
00278         return session;
00279 }
00280 
00281 static void main_loop(struct pam_nufw_s *pn_s)
00282 {
00283         int connected = 1;
00284         int tempo = 1;
00285         unsigned long interval = 100;
00286 
00287         for (;;) {
00288                 if (!connected) {
00289                         sleep(tempo);
00290                         if (tempo < MAX_RETRY_TIME) {
00291                                 tempo = tempo * 2;
00292                         }
00293 
00294                         if (nu_client_connect
00295                             (session, pn_s->nuauth_srv, pn_s->nuauth_port,
00296                              pn_s->err) != 0) {
00297                                 tempo = 1;
00298                                 connected = 1;
00299                         } else {
00300                                 nu_client_reset(session);
00301                                 /* quit if password is wrong. to not lock user account */
00302                                 syslog(LOG_ERR,
00303                                        "(pam_nufw) unable to reconnect to server: %s",
00304                                        nu_client_strerror(session, pn_s->err));
00305                                 if (pn_s->err->error ==
00306                                     BAD_CREDENTIALS_ERR) {
00307                                         syslog(LOG_ERR,
00308                                                "(pam_nufw) bad credentials: leaving");
00309                                         exit_client();
00310                                 }
00311                         }
00312                 } else {
00313                         if (nu_client_check(session, pn_s->err) < 0) {
00314                                 nu_client_reset(session);
00315                                 connected = 0;
00316                                 syslog(LOG_ERR,
00317                                        "(pam_nufw) libnuclient error: %s",
00318                                        nu_client_strerror(session, pn_s->err));
00319                         }
00320                 }
00321         }
00322 }
00323 
00324 struct user_info_s {
00325         const char *username;
00326         const char *password;
00327         uid_t uid;
00328         gid_t gid;
00329         char *home_dir;
00330 };
00331 
00332 static void clear_user_info(struct user_info_s *user_info)
00333 {
00334         memset(user_info, 0, sizeof(*user_info));
00335 }
00336 
00337 static int nufw_client_func(struct pam_nufw_s *pn_s,
00338                             struct user_info_s *user_info)
00339 {
00340         int mypid;
00341         FILE *RunD;
00342         struct sigaction no_action;
00343         int res_err;
00344 
00345         /* set user and group identifier, and home directory */
00346         if (setuid(user_info->uid) != 0) {
00347                 syslog(LOG_ERR, "(pam_nufw) Fail to set sigaction");
00348                 return PAM_AUTH_ERR;
00349         }
00350         setgid(user_info->gid);
00351         setenv("HOME", user_info->home_dir, 1);
00352 
00353         /* catch SIGINT and SIGTERM signals, install handler: exit_client() */
00354         no_action.sa_handler = exit_client;
00355         sigemptyset(&(no_action.sa_mask));
00356         no_action.sa_flags = 0;
00357         if (sigaction(SIGINT, &no_action, NULL) != 0
00358             || sigaction(SIGTERM, &no_action, NULL) != 0) {
00359                 syslog(LOG_ERR, "(pam_nufw) Fail to set sigaction");
00360                 return PAM_AUTH_ERR;
00361         }
00362 
00363         /* init nuclient_error_t */
00364         res_err = nu_client_error_init(&pn_s->err);
00365         if (res_err != 0) {
00366                 syslog(LOG_ERR,
00367                        "(pam_nufw) Cannot init error structure! %i",
00368                        res_err);
00369                 return PAM_AUTH_ERR;
00370         }
00371 
00372         /* libnuclient init function */
00373         if (!nu_client_global_init(pn_s->err)) {
00374                 syslog(LOG_ERR,
00375                        "(pam_nufw) Cannot init nuclient library: %s",
00376                        nu_client_strerror(session, pn_s->err));
00377                 return PAM_AUTH_ERR;
00378         }
00379 
00380         /* create libnuclient session (connection to nuauth) */
00381         session =
00382             do_connect(nu_client_to_utf8
00383                        (user_info->username, locale_charset),
00384                        nu_client_to_utf8(user_info->password,
00385                                          locale_charset), pn_s->err);
00386         clear_user_info(user_info);
00387 
00388         /* fails to connect to nuauth? */
00389         if (session == NULL) {
00390                 int errno_copy = errno;
00391                 syslog(LOG_ERR,
00392                        "(pam_nufw) Cannot connect to nuauth_session_t Server");
00393                 syslog(LOG_ERR, "(pam_nufw) Problem: %s\n",
00394                        strerror(errno_copy));
00395                 return PAM_SUCCESS;     /* PAM_AUTH_ERR */
00396         }
00397 
00398         /* session opened to nuauth: write pid in lockfile */
00399         mypid = getpid();
00400         RunD = fopen(_get_runpid(pn_s, user_info->home_dir), "w");
00401         if (RunD != NULL) {
00402                 fprintf(RunD, "%d", mypid);
00403                 fclose(RunD);
00404                 syslog(LOG_INFO,
00405                        "(pam_nufw) session to Nuauth server opened, username=%s, server=%s (pid=%lu)",
00406                        user_info->username, pn_s->nuauth_srv,
00407                        (unsigned long) mypid);
00408         }
00409 
00410         /* and then stay in main loop ... */
00411         main_loop(pn_s);
00412         return PAM_SUCCESS;
00413 }
00414 
00415 static int read_user_info(struct user_info_s *user_info,
00416                           pam_handle_t * pamh,
00417                           int argc, const char **argv, int *pam_result)
00418 {
00419         struct passwd *pw;
00420         int ctrl;
00421 
00422         /* init. pam with pam arguments */
00423         ctrl = _pam_parse(argc, argv, &pn_s);
00424 
00425         /* read user name */
00426         *pam_result = pam_get_user(pamh, &user_info->username, NULL);
00427         if (*pam_result != PAM_SUCCESS) {
00428                 syslog(LOG_ERR, "get user returned error: %s",
00429                        pam_strerror(pamh, *pam_result));
00430                 *pam_result = PAM_AUTH_ERR;
00431                 return 0;
00432         }
00433 
00434         /* if not username is specified, use default username */
00435         if (user_info->username == NULL || user_info->username[0] == '\0') {
00436                 user_info->username = DEFAULT_USER;
00437                 pam_set_item(pamh, PAM_USER, DEFAULT_USER);
00438         }
00439 
00440         /* Test if we have to make a connection on nuauth for this user */
00441         if (do_auth_on_user(user_info->username) != 0) {
00442                 syslog(LOG_INFO, "(pam_nufw) no auth for user %s",
00443                        user_info->username);
00444                 *pam_result = PAM_SUCCESS;
00445                 return 0;
00446         }
00447 
00448         /* read user password */
00449         if (pam_get_item
00450             (pamh, PAM_AUTHTOK,
00451              (const void **) &user_info->password) == PAM_SUCCESS) {
00452                 if (user_info->password == NULL)
00453                         syslog(LOG_ERR, "(pam_nufw) password is NULL!");
00454         } else {
00455                 syslog(LOG_ERR, "pam_nufw failed to get password");
00456                 *pam_result = PAM_AUTH_ERR;
00457                 return 0;
00458         }
00459 
00460         /* read password, user and group identifier */
00461         pw = (struct passwd *) getpwnam(user_info->username);
00462         user_info->uid = pw->pw_uid;
00463         user_info->gid = pw->pw_gid;
00464         user_info->home_dir = pw->pw_dir;
00465         *pam_result = PAM_SUCCESS;
00466         return 1;
00467 }
00468 
00469 /*
00470  * used to open the connection to the nuauth server
00471  */
00472 PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh, int flags,
00473                             int argc, const char **argv)
00474 {
00475         int retval;
00476         struct user_info_s user_info;
00477         char *errmsg;
00478         pid_t child_pid;
00479 
00480         syslog(LOG_ERR, "(pam_nufw) do authenticate");
00481 
00482         /* check libnuclient version */
00483         if (!nu_check_version(NUCLIENT_VERSION)) {
00484                 syslog(LOG_ERR,
00485                        "(pam nufw) Wrong version of libnuclient (%s instead of %s)",
00486                        nu_get_version(), NUCLIENT_VERSION);
00487                 return PAM_AUTH_ERR;
00488         }
00489 
00490         /* init. our structure */
00491         errmsg = _init_pam_nufw_s(&pn_s);
00492         if (errmsg != NULL) {
00493                 syslog(LOG_ERR, "(pam nufw) init failure: %s", errmsg);
00494                 return PAM_AUTH_ERR;
00495         }
00496 
00497         /* test if lock file exists */
00498         if (!access(pn_s.file_lock, R_OK)) {
00499                 FILE *fd;
00500                 if ((fd = fopen(pn_s.file_lock, "r"))) {
00501                         char line[20];
00502                         if (fgets(line, 19, fd)) {
00503                                 pid_t pid = (pid_t) atoi(line);
00504                                 fclose(fd);
00505                                 if (kill(pid, 0)) {
00506                                         unlink(pn_s.file_lock);
00507                                 } else {
00508                                         return PAM_SUCCESS;
00509                                 }
00510                         }
00511                 }
00512         }
00513 
00514         /* test if program is running */
00515 
00516         /* read user informations */
00517         if (!read_user_info(&user_info, pamh, argc, argv, &retval)) {
00518                 clear_user_info(&user_info);
00519                 return retval;
00520         }
00521 
00522         /* do fork */
00523         child_pid = fork();
00524         if (child_pid < 0) {
00525                 syslog(LOG_ERR, "(pam_nufw) fork failed");
00526                 clear_user_info(&user_info);
00527                 return PAM_AUTH_ERR;
00528         }
00529 
00530         if (child_pid != 0) {
00531                 /* in fork parent */
00532                 retval = PAM_SUCCESS;
00533         } else {
00534                 /* in fork child */
00535                 retval = nufw_client_func(&pn_s, &user_info);
00536         }
00537         clear_user_info(&user_info);
00538         return retval;
00539 }
00540 
00541 PAM_EXTERN int pam_sm_setcred(pam_handle_t * pamh, int flags, int argc,
00542                        const char **argv)
00543 {
00544         /*D(("pam_nufw sm_setcred")); */
00545         return PAM_SUCCESS;
00546 }
00547 
00548 /* --- account management functions --- */
00549 
00550 PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t * pamh, int flags, int argc, const char **argv)
00551 {
00552         D(("pam_nufw sm_acct_mgmt"));
00553         return PAM_SUCCESS;
00554 }
00555 
00556 /* --- password management --- */
00557 
00558 PAM_EXTERN
00559 int pam_sm_chauthtok(pam_handle_t * pamh, int flags, int argc,
00560                          const char **argv)
00561 {
00562         D(("pam_nufw sm_chauthok"));
00563         return PAM_SUCCESS;
00564 }
00565 
00566 /* --- session management --- */
00567 
00568 PAM_EXTERN
00569 int pam_sm_open_session(pam_handle_t * pamh, int flags, int argc,
00570                             const char **argv)
00571 {
00572         syslog(LOG_INFO, "(pam_nufw) session opened");
00573         return PAM_SUCCESS;
00574 }
00575 
00576 /*
00577  * On session closing, we want to close the connection
00578  * -> get pid file, and kill process
00579  */
00580 PAM_EXTERN
00581 int pam_sm_close_session(pam_handle_t * pamh, int flags, int argc,
00582                              const char **argv)
00583 {
00584         /*D(("pam_nufw sm_close_session")); */
00585         int ctrl;
00586         struct passwd *pw;
00587         const char *user = NULL;
00588         char *errmsg;
00589         int retval;
00590 
00591         /* get parameters */
00592         errmsg = _init_pam_nufw_s(&pn_s);
00593         if (errmsg != NULL) {
00594                 syslog(LOG_ERR, "(pam nufw) init failure: %s", errmsg);
00595                 return PAM_AUTH_ERR;
00596         }
00597 
00598         /*syslog(LOG_INFO, "(pam_nufw) file_lock: %s",pn_s.file_lock); */
00599         ctrl = _pam_parse(argc, argv, &pn_s);
00600 
00601         /* get username */
00602         retval = pam_get_user(pamh, &user, NULL);
00603         if (do_auth_on_user(user) != 0) {
00604                 return PAM_SUCCESS;
00605         }
00606         pw = (struct passwd *) getpwnam(user);
00607         setenv("HOME", pw->pw_dir, 1);
00608         /*syslog(LOG_INFO, "(pam_nufw) file_lock: %s",_get_runpid(&pn_s)); */
00609 
00610         /* kill client */
00611         _kill_nuclient(_get_runpid(&pn_s, pw->pw_dir));
00612 
00613         syslog(LOG_INFO, "(pam_nufw) session closed");
00614         return PAM_SUCCESS;
00615 }
00616 
00617 /* end of module definition */
00618 
00619 #ifdef PAM_STATIC
00620 
00621 /* static module data */
00622 
00623 struct pam_module _pam_permit_modstruct = {
00624         "pam_nufw",
00625         pam_sm_authenticate,
00626         pam_sm_setcred,
00627         pam_sm_acct_mgmt,
00628         pam_sm_open_session,
00629         pam_sm_close_session,
00630         pam_sm_chauthtok
00631 };
00632 
00633 #endif

Generated on Sat Nov 22 04:00:37 2008 for NuFW by  doxygen 1.4.7