(root)/
Linux-PAM-1.5.3/
modules/
pam_env/
tst-pam_env-retval.c
       1  /*
       2   * Check pam_env return values.
       3   *
       4   * Copyright (c) 2020-2022 Dmitry V. Levin <ldv@altlinux.org>
       5   * Copyright (c) 2022 Stefan Schubert <schubi@suse.de>
       6   */
       7  
       8  #include "test_assert.h"
       9  
      10  #include <errno.h>
      11  #include <libgen.h>
      12  #include <limits.h>
      13  #include <stdio.h>
      14  #include <stdlib.h>
      15  #include <string.h>
      16  #include <unistd.h>
      17  #include <sys/stat.h>
      18  #include <security/pam_appl.h>
      19  
      20  #define MODULE_NAME "pam_env"
      21  #define TEST_NAME "tst-" MODULE_NAME "-retval"
      22  #define TEST_NAME_DIR TEST_NAME ".dir"
      23  
      24  static const char service_file[] = TEST_NAME ".service";
      25  static const char missing_file[] = TEST_NAME ".missing";
      26  static const char my_conf[] = TEST_NAME ".conf";
      27  static const char my_env[] = TEST_NAME ".env";
      28  #ifdef VENDORDIR
      29  static const char dir_usr_etc_security[] = TEST_NAME_DIR VENDOR_SCONFIGDIR;
      30  static const char usr_env[] = TEST_NAME_DIR VENDORDIR "/environment";
      31  static const char usr_conf[] = TEST_NAME_DIR VENDOR_SCONFIGDIR "/pam_env.conf";
      32  #endif
      33  
      34  static struct pam_conv conv;
      35  
      36  #ifdef VENDORDIR
      37  static void
      38  mkdir_p(const char *pathname, mode_t mode)
      39  {
      40  	if (mkdir(pathname, mode) == 0 || errno == EEXIST)
      41  		return;
      42  	ASSERT_EQ(errno, ENOENT);
      43  
      44  	char *buf;
      45  	ASSERT_NE(NULL, buf = strdup(pathname));
      46  	mkdir_p(dirname(buf), mode);
      47  	free(buf);
      48  
      49  	ASSERT_EQ(0, mkdir(pathname, mode));
      50  }
      51  
      52  static void
      53  rmdir_p(const char *pathname)
      54  {
      55  	if (rmdir(pathname) != 0)
      56  		return;
      57  
      58  	char *buf;
      59  	ASSERT_NE(NULL, buf = strdup(pathname));
      60  	rmdir_p(dirname(buf));
      61  	free(buf);
      62  }
      63  #endif
      64  
      65  static void
      66  setup(void)
      67  {
      68  	FILE *fp;
      69  
      70  	ASSERT_NE(NULL, fp = fopen(my_conf, "w"));
      71  	ASSERT_LT(0, fprintf(fp,
      72  			     "EDITOR\tDEFAULT=vim\n"
      73  			     "PAGER\tDEFAULT=more\n"));
      74  	ASSERT_EQ(0, fclose(fp));
      75  
      76  	ASSERT_NE(NULL, fp = fopen(my_env, "w"));
      77  	ASSERT_LT(0, fprintf(fp,
      78  			     "test_value=foo\n"
      79  			     "test2_value=bar\n"));
      80  	ASSERT_EQ(0, fclose(fp));
      81  
      82  #ifdef VENDORDIR
      83  	mkdir_p(dir_usr_etc_security, 0755);
      84  
      85  	ASSERT_NE(NULL, fp = fopen(usr_env, "w"));
      86  	ASSERT_LT(0, fprintf(fp,
      87  			     "usr_etc_test=foo\n"
      88  			     "usr_etc_test2=bar\n"));
      89  	ASSERT_EQ(0, fclose(fp));
      90  
      91  	ASSERT_NE(NULL, fp = fopen(usr_conf, "w"));
      92  	ASSERT_LT(0, fprintf(fp,
      93  			     "PAGER		DEFAULT=emacs\n"
      94  			     "MANPAGER		DEFAULT=less\n"));
      95  	ASSERT_EQ(0, fclose(fp));
      96  #endif
      97  }
      98  
      99  static void
     100  cleanup(void)
     101  {
     102  	ASSERT_EQ(0, unlink(my_conf));
     103  	ASSERT_EQ(0, unlink(my_env));
     104  #ifdef VENDORDIR
     105  	ASSERT_EQ(0, unlink(usr_env));
     106  	ASSERT_EQ(0, unlink(usr_conf));
     107  	rmdir_p(dir_usr_etc_security);
     108  #endif
     109  }
     110  
     111  static void
     112  check_array(const char **array1, char **array2)
     113  {
     114  	for (const char **a1 = array1; *a1 != NULL; ++a1) {
     115  		char **a2;
     116  		for (a2 = array2; *a2 != NULL; ++a2) {
     117  			if (strcmp(*a1, *a2) == 0)
     118  				break;
     119  		}
     120  		ASSERT_NE(NULL, *a2);
     121  	}
     122  }
     123  
     124  static void
     125  check_env(const char **list)
     126  {
     127  	pam_handle_t *pamh = NULL;
     128  
     129  	ASSERT_EQ(PAM_SUCCESS,
     130  		  pam_start_confdir(service_file, "", &conv, ".", &pamh));
     131  	ASSERT_NE(NULL, pamh);
     132  
     133  	ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0));
     134  
     135  	char **env_list = pam_getenvlist(pamh);
     136  	ASSERT_NE(NULL, env_list);
     137  
     138  	check_array(list, env_list);
     139  
     140  	for (char **e = env_list; *e != NULL; ++e)
     141  		free(*e);
     142  	free(env_list);
     143  
     144  	ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0));
     145  	ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
     146  }
     147  
     148  int
     149  main(void)
     150  {
     151  	pam_handle_t *pamh = NULL;
     152  	FILE *fp;
     153  	char cwd[PATH_MAX];
     154  
     155  	ASSERT_NE(NULL, getcwd(cwd, sizeof(cwd)));
     156  
     157  	setup();
     158  
     159  	/*
     160  	 * When conffile= specifies a missing file, all methods except
     161  	 * pam_sm_acct_mgmt and pam_sm_chauthtok return PAM_IGNORE.
     162  	 * The return code of the stack where every module returns PAM_IGNORE
     163  	 * is PAM_PERM_DENIED.
     164  	 */
     165  	ASSERT_NE(NULL, fp = fopen(service_file, "w"));
     166  	ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
     167  			     "auth required %s/.libs/%s.so conffile=%s/%s\n"
     168  			     "account required %s/.libs/%s.so conffile=%s/%s\n"
     169  			     "password required %s/.libs/%s.so conffile=%s/%s\n"
     170  			     "session required %s/.libs/%s.so conffile=%s/%s\n",
     171  			     cwd, MODULE_NAME, cwd, missing_file,
     172  			     cwd, MODULE_NAME, cwd, missing_file,
     173  			     cwd, MODULE_NAME, cwd, missing_file,
     174  			     cwd, MODULE_NAME, cwd, missing_file));
     175  	ASSERT_EQ(0, fclose(fp));
     176  
     177  	ASSERT_EQ(PAM_SUCCESS,
     178  		  pam_start_confdir(service_file, "", &conv, ".", &pamh));
     179  	ASSERT_NE(NULL, pamh);
     180  	ASSERT_EQ(PAM_PERM_DENIED, pam_authenticate(pamh, 0));
     181  	ASSERT_EQ(PAM_PERM_DENIED, pam_setcred(pamh, 0));
     182  	ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0));
     183  	ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0));
     184  	ASSERT_EQ(PAM_PERM_DENIED, pam_open_session(pamh, 0));
     185  	ASSERT_EQ(PAM_PERM_DENIED, pam_close_session(pamh, 0));
     186  	ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
     187  	pamh = NULL;
     188  
     189  	/*
     190  	 * When conffile= specifies a missing file, all methods except
     191  	 * pam_sm_acct_mgmt and pam_sm_chauthtok return PAM_IGNORE.
     192  	 * pam_permit is added after pam_env to convert PAM_IGNORE to PAM_SUCCESS.
     193  	 */
     194  	ASSERT_NE(NULL, fp = fopen(service_file, "w"));
     195  	ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
     196  			     "auth required %s/.libs/%s.so conffile=%s/%s\n"
     197  			     "auth required %s/../pam_permit/.libs/pam_permit.so\n"
     198  			     "account required %s/.libs/%s.so conffile=%s/%s\n"
     199  			     "account required %s/../pam_permit/.libs/pam_permit.so\n"
     200  			     "password required %s/.libs/%s.so conffile=%s/%s\n"
     201  			     "password required %s/../pam_permit/.libs/pam_permit.so\n"
     202  			     "session required %s/.libs/%s.so conffile=%s/%s\n"
     203  			     "session required %s/../pam_permit/.libs/pam_permit.so\n",
     204  			     cwd, MODULE_NAME, cwd, missing_file, cwd,
     205  			     cwd, MODULE_NAME, cwd, missing_file, cwd,
     206  			     cwd, MODULE_NAME, cwd, missing_file, cwd,
     207  			     cwd, MODULE_NAME, cwd, missing_file, cwd));
     208  	ASSERT_EQ(0, fclose(fp));
     209  
     210  	ASSERT_EQ(PAM_SUCCESS,
     211  		  pam_start_confdir(service_file, "", &conv, ".", &pamh));
     212  	ASSERT_NE(NULL, pamh);
     213  	ASSERT_EQ(PAM_SUCCESS, pam_authenticate(pamh, 0));
     214  	ASSERT_EQ(PAM_SUCCESS, pam_setcred(pamh, 0));
     215  	ASSERT_EQ(PAM_SERVICE_ERR, pam_acct_mgmt(pamh, 0));
     216  	ASSERT_EQ(PAM_SERVICE_ERR, pam_chauthtok(pamh, 0));
     217  	ASSERT_EQ(PAM_SUCCESS, pam_open_session(pamh, 0));
     218  	ASSERT_EQ(PAM_SUCCESS, pam_close_session(pamh, 0));
     219  	ASSERT_EQ(PAM_SUCCESS, pam_end(pamh, 0));
     220  	pamh = NULL;
     221  
     222  	/*
     223  	 * conffile= specifies an existing file,
     224  	 * envfile= specifies an empty file.
     225  	 */
     226  	ASSERT_NE(NULL, fp = fopen(service_file, "w"));
     227  	ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
     228  			     "session required %s/.libs/%s.so"
     229  			     " conffile=%s/%s envfile=%s\n",
     230  			     cwd, MODULE_NAME,
     231  			     cwd, my_conf, "/dev/null"));
     232  	ASSERT_EQ(0, fclose(fp));
     233  
     234  	const char *env1[] = { "EDITOR=vim", "PAGER=more", NULL };
     235  	check_env(env1);
     236  
     237  	/*
     238  	 * conffile= specifies an empty file,
     239  	 * envfile= specifies an existing file.
     240  	 */
     241  	ASSERT_NE(NULL, fp = fopen(service_file, "w"));
     242  	ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
     243  			     "session required %s/.libs/%s.so"
     244  			     " conffile=%s envfile=%s/%s\n",
     245  			     cwd, MODULE_NAME,
     246  			     "/dev/null", cwd, my_env));
     247  	ASSERT_EQ(0, fclose(fp));
     248  
     249  	const char *env2[] = { "test_value=foo", "test2_value=bar", NULL };
     250  	check_env(env2);
     251  
     252  #if defined (USE_ECONF) && defined (VENDORDIR)
     253  
     254  	/* envfile is a directory. So values will be read from {TEST_NAME_DIR}/usr/etc and {TEST_NAME_DIR}/etc */
     255  	ASSERT_NE(NULL, fp = fopen(service_file, "w"));
     256  	ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
     257  			     "session required %s/.libs/%s.so"
     258  			     " conffile=%s envfile=%s/%s/\n",
     259  			     cwd, MODULE_NAME,
     260  			     "/dev/null",
     261  			     cwd, TEST_NAME_DIR));
     262  	ASSERT_EQ(0, fclose(fp));
     263  
     264  	const char *env3[] = {"usr_etc_test=foo", "usr_etc_test2=bar", NULL};
     265  	check_env(env3);
     266  
     267  	/* conffile is a directory. So values will be read from {TEST_NAME_DIR}/usr/etc and {TEST_NAME_DIR}/etc */
     268  	ASSERT_NE(NULL, fp = fopen(service_file, "w"));
     269  	ASSERT_LT(0, fprintf(fp, "#%%PAM-1.0\n"
     270  			     "session required %s/.libs/%s.so"
     271  			     " conffile=%s/%s/ envfile=%s\n",
     272  			     cwd, MODULE_NAME,
     273  			     cwd, TEST_NAME_DIR,
     274  			     "/dev/null"));
     275  	ASSERT_EQ(0, fclose(fp));
     276  
     277  	const char *env4[] = {"PAGER=emacs", "MANPAGER=less", NULL};
     278  	check_env(env4);
     279  
     280  #endif
     281  
     282  	/* cleanup */
     283  	cleanup();
     284  	ASSERT_EQ(0, unlink(service_file));
     285  
     286  	return 0;
     287  }