Exemplo n.º 1
0
PAMH_ARG_DECL(int get_account_info,
	const char *name, struct passwd **pwd, struct spwd **spwdent)
{
	/* UNIX passwords area */
	*pwd = pam_modutil_getpwnam(pamh, name);	/* Get password file entry... */
	*spwdent = NULL;

	if (*pwd != NULL) {
		if (strcmp((*pwd)->pw_passwd, "*NP*") == 0)
		{ /* NIS+ */
#ifdef HELPER_COMPILE
			uid_t save_euid, save_uid;

			save_euid = geteuid();
			save_uid = getuid();
			if (save_uid == (*pwd)->pw_uid)
				setreuid(save_euid, save_uid);
			else  {
				setreuid(0, -1);
				if (setreuid(-1, (*pwd)->pw_uid) == -1) {
					setreuid(-1, 0);
					setreuid(0, -1);
					if(setreuid(-1, (*pwd)->pw_uid) == -1)
						return PAM_CRED_INSUFFICIENT;
				}
			}

			*spwdent = pam_modutil_getspnam(pamh, name);
			if (save_uid == (*pwd)->pw_uid)
				setreuid(save_uid, save_euid);
			else {
				setreuid(-1, 0);
				setreuid(save_uid, -1);
				setreuid(-1, save_euid);
			}

			if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
				return PAM_AUTHINFO_UNAVAIL;
#else
			/* we must run helper for NIS+ passwords */
			return PAM_UNIX_RUN_HELPER;
#endif
		} else if (is_pwd_shadowed(*pwd)) {
			/*
			 * ...and shadow password file entry for this user,
			 * if shadowing is enabled
			 */
#ifndef HELPER_COMPILE
			if (geteuid() || SELINUX_ENABLED)
				return PAM_UNIX_RUN_HELPER;
#endif
			*spwdent = pam_modutil_getspnam(pamh, name);
			if (*spwdent == NULL || (*spwdent)->sp_pwdp == NULL)
				return PAM_AUTHINFO_UNAVAIL;
		}
	} else {
		return PAM_USER_UNKNOWN;
	}
	return PAM_SUCCESS;
}
Exemplo n.º 2
0
/* now the session stuff */
PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
					int argc, const char **argv)
{
	pid_t pid;
	int ctrl, ret;
	char *user_name;
	struct passwd *pwd;

	D(("called."));

	ctrl = _pam_parse(pamh, argc, argv);

	ret = pam_get_item(pamh, PAM_USER, (void *) &user_name);
	if (user_name == NULL || ret != PAM_SUCCESS)  {
		pam_syslog(pamh, LOG_ERR, "open_session - error recovering"
				"username");
		return PAM_SESSION_ERR;
	}

	pwd = pam_modutil_getpwnam(pamh, user_name);
	if (!pwd) {
		if (ctrl & PAM_DEBUG_ARG)
			pam_syslog(pamh, LOG_ERR, "open_session username"
					" '%s' does not exist", user_name);
		return PAM_SESSION_ERR;
	}

	D(("user name is %s", user_name));

	/* Initialize libcg */
	ret = cgroup_init();
	if (ret) {
		if (ctrl & PAM_DEBUG_ARG)
			pam_syslog(pamh, LOG_ERR, "libcgroup initialization"
							" failed");
		return PAM_SESSION_ERR;
	}

	D(("Initialized libcgroup successfuly."));

	/* Determine the pid of the task */
	pid = getpid();

	/* Note: We are using default gid here. Is there a way to determine
	 * under what egid service will be provided?
	 */
	ret = cgroup_change_cgroup_uid_gid(pwd->pw_uid, pwd->pw_gid, pid);
	if (ret) {
		if (ctrl & PAM_DEBUG_ARG)
			pam_syslog(pamh, LOG_ERR, "Change of cgroup for process"
				" with username %s failed.\n", user_name);
		return PAM_SESSION_ERR;
	}

	if (ctrl & PAM_DEBUG_ARG)
		pam_syslog(pamh, LOG_DEBUG, "Changed cgroup for process %d"
				"  with username %s.\n", pid, user_name);

	return PAM_SUCCESS;
}
Exemplo n.º 3
0
PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags,
		    int argc, const char **argv)
{
    int retval, ctrl;
    const void *user;
    const struct passwd *pwd;
    uid_t uid;
    time_t lltime = 0;

    /*
     * this module gets the uid of the PAM_USER. Uses it to display
     * last login info and then updates the lastlog for that user.
     */

    ctrl = _pam_parse(pamh, flags, argc, argv);

    /* which user? */

    retval = pam_get_item(pamh, PAM_USER, &user);
    if (retval != PAM_SUCCESS || user == NULL || *(const char *)user == '\0') {
	pam_syslog(pamh, LOG_NOTICE, "user unknown");
	return PAM_USER_UNKNOWN;
    }

    /* what uid? */

    pwd = pam_modutil_getpwnam (pamh, user);
    if (pwd == NULL) {
	D(("couldn't identify user %s", user));
	return PAM_USER_UNKNOWN;
    }
    uid = pwd->pw_uid;
    pwd = NULL;                                         /* tidy up */

    /* process the current login attempt (indicate last) */

    retval = last_login_date(pamh, ctrl, uid, user, &lltime);

    if ((ctrl & LASTLOG_BTMP) && retval == PAM_SUCCESS) {
	    retval = last_login_failed(pamh, ctrl, user, lltime);
    }

    /* indicate success or failure */

    uid = -1;                                           /* forget this */

    return retval;
}
Exemplo n.º 4
0
static int init(pam_handle_t *pamh, struct pld_cfg *cfg, struct pld_ctx **ctx,
                const char **username, const char **service, const char **ruser,
                const char **rhost, const char **tty)
{
  int rc;
  struct passwd *pwent;
  /* get user name */
  rc = pam_get_user(pamh, username, NULL);
  if (rc != PAM_SUCCESS)
  {
    pam_syslog(pamh, LOG_ERR, "failed to get user name: %s", pam_strerror(pamh, rc));
    return rc;
  }
  if ((*username == NULL) || ((*username)[0] == '\0'))
  {
    pam_syslog(pamh, LOG_ERR, "got empty user name");
    return PAM_USER_UNKNOWN;
  }
  /* check uid */
  if (cfg->minimum_uid > 0)
  {
    pwent = pam_modutil_getpwnam(args->pamh, *username);
    if ((pwent != NULL) && (pwent->pw_uid < cfg->minimum_uid))
    {
      if (cfg->debug)
        pam_syslog(pamh, LOG_DEBUG, "uid below minimum_uid; user=%s uid=%ld",
                   *username, (long)pwent->pw_uid);
      return cfg->ignore_unknown_user ? PAM_IGNORE : PAM_USER_UNKNOWN;
    }
  }
  /* get our context */
  rc = ctx_get(pamh, *username, ctx);
  if (rc != PAM_SUCCESS)
    return rc;
  /* get service name */
  rc = pam_get_item(pamh, PAM_SERVICE, (PAM_ITEM_CONST void **)service);
  if (rc != PAM_SUCCESS)
  {
    pam_syslog(pamh, LOG_ERR, "failed to get service name: %s",
               pam_strerror(pamh, rc));
    return rc;
  }
  /* get more PAM information (ignore errors) */
  pam_get_item(pamh, PAM_RUSER, (PAM_ITEM_CONST void **)ruser);
  pam_get_item(pamh, PAM_RHOST, (PAM_ITEM_CONST void **)rhost);
  pam_get_item(pamh, PAM_TTY, (PAM_ITEM_CONST void **)tty);
  return PAM_SUCCESS;
}
Exemplo n.º 5
0
/*
 * Given the PAM arguments and the user we're authenticating, see if we should
 * ignore that user because they're root or have a low-numbered UID and we
 * were configured to ignore such users.  Returns true if we should ignore
 * them, false otherwise.
 */
int
pamk5_should_ignore(struct pam_args *args, PAM_CONST char *username)
{
    struct passwd *pwd;

    if (args->ignore_root && strcmp("root", username) == 0) {
        pamk5_debug(args, "ignoring root user");
        return 1;
    }
    if (args->minimum_uid > 0) {
        pwd = pam_modutil_getpwnam(args->pamh, username);
        if (pwd != NULL && pwd->pw_uid < (unsigned long) args->minimum_uid) {
            pamk5_debug(args, "ignoring low-UID user (%lu < %d)",
                        (unsigned long) pwd->pw_uid, args->minimum_uid);
            return 1;
        }
    }
    return 0;
}
Exemplo n.º 6
0
PAM_EXTERN
int pam_sm_authenticate (pam_handle_t *pamh, int flags, int argc,
			 const char **argv)
{
    const char *luser = NULL;
    const char *ruser = NULL, *rhost = NULL;
    const char *opt_superuser = NULL;
    const void *c_void;
    int opt_debug = 0;
    int opt_silent;
    int as_root;
    int retval;

    opt_silent = flags & PAM_SILENT;

    while (argc-- > 0) {
      if (strcmp(*argv, "debug") == 0)
	opt_debug = 1;
      else if (strcmp (*argv, "silent") == 0 || strcmp(*argv, "suppress") == 0)
	opt_silent = 1;
      else if (strncmp(*argv, "superuser="******"superuser="******"superuser="******"unrecognized option '%s'", *argv);

      ++argv;
    }

    retval = pam_get_item (pamh, PAM_RHOST, &c_void);
    if (retval != PAM_SUCCESS) {
      pam_syslog(pamh, LOG_ERR, "could not get the remote host name");
      return retval;
    }
    rhost = c_void;

    retval = pam_get_item(pamh, PAM_RUSER, &c_void);
    ruser = c_void;
    if (retval != PAM_SUCCESS) {
      pam_syslog(pamh, LOG_ERR, "could not get the remote username");
      return retval;
    }

    retval = pam_get_user(pamh, &luser, NULL);
    if (retval != PAM_SUCCESS) {
      pam_syslog(pamh, LOG_ERR, "could not determine name of local user");
      return retval;
    }

    if (rhost == NULL || ruser == NULL || luser == NULL)
      return PAM_AUTH_ERR;

    if (opt_superuser && strcmp(opt_superuser, luser) == 0)
      as_root = 1;
    else {
      struct passwd *lpwd;

      lpwd = pam_modutil_getpwnam(pamh, luser);
      if (lpwd == NULL) {
	if (opt_debug)
	  /* don't print by default, could be the user's password */
	  pam_syslog(pamh, LOG_DEBUG,
		     "user '%s' unknown to this system", luser);
	return PAM_USER_UNKNOWN;

      }
      as_root = (lpwd->pw_uid == 0);
    }

#ifdef HAVE_RUSEROK_AF
    retval = ruserok_af (rhost, as_root, ruser, luser, PF_UNSPEC);
#else
    retval = ruserok (rhost, as_root, ruser, luser);
#endif
    if (retval != 0) {
      if (!opt_silent || opt_debug)
	pam_syslog(pamh, LOG_WARNING, "denied access to %s@%s as %s",
		   ruser, rhost, luser);
      return PAM_AUTH_ERR;
    } else {
      if (!opt_silent || opt_debug)
	pam_syslog(pamh, LOG_NOTICE, "allowed access to %s@%s as %s",
		   ruser, rhost, luser);
      return PAM_SUCCESS;
    }
}
Exemplo n.º 7
0
/* now the session stuff */
PAM_EXTERN int
pam_sm_open_session (pam_handle_t *pamh, int flags UNUSED,
		     int argc, const char **argv)
{
    int retval;
    int i;
    int glob_rc;
    char *user_name;
    struct passwd *pwd;
    int ctrl;
    struct pam_limit_s plstruct;
    struct pam_limit_s *pl = &plstruct;
    glob_t globbuf;
    const char *oldlocale;

    D(("called."));

    memset(pl, 0, sizeof(*pl));
    memset(&globbuf, 0, sizeof(globbuf));

    ctrl = _pam_parse(pamh, argc, argv, pl);
    retval = pam_get_item( pamh, PAM_USER, (void*) &user_name );
    if ( user_name == NULL || retval != PAM_SUCCESS ) {
        pam_syslog(pamh, LOG_CRIT, "open_session - error recovering username");
        return PAM_SESSION_ERR;
     }

    pwd = pam_modutil_getpwnam(pamh, user_name);
    if (!pwd) {
        if (ctrl & PAM_DEBUG_ARG)
            pam_syslog(pamh, LOG_WARNING,
		       "open_session username '%s' does not exist", user_name);
        return PAM_USER_UNKNOWN;
    }

    retval = init_limits(pamh, pl, ctrl);
    if (retval != PAM_SUCCESS) {
        pam_syslog(pamh, LOG_WARNING, "cannot initialize");
        return PAM_ABORT;
    }

    retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
    if (retval == PAM_IGNORE) {
	D(("the configuration file ('%s') has an applicable '<domain> -' entry", CONF_FILE));
	return PAM_SUCCESS;
    }
    if (retval != PAM_SUCCESS || pl->conf_file != NULL)
	/* skip reading limits.d if config file explicitely specified */
	goto out;

    /* Read subsequent *.conf files, if they exist. */

    /* set the LC_COLLATE so the sorting order doesn't depend
	on system locale */

    oldlocale = setlocale(LC_COLLATE, "C");
    glob_rc = glob(LIMITS_CONF_GLOB, GLOB_ERR, NULL, &globbuf);

    if (oldlocale != NULL)
	setlocale (LC_COLLATE, oldlocale);

    if (!glob_rc) {
	/* Parse the *.conf files. */
	for (i = 0; globbuf.gl_pathv[i] != NULL; i++) {
	    pl->conf_file = globbuf.gl_pathv[i];
    	    retval = parse_config_file(pamh, pwd->pw_name, pwd->pw_uid, pwd->pw_gid, ctrl, pl);
    	    if (retval == PAM_IGNORE) {
		D(("the configuration file ('%s') has an applicable '<domain> -' entry", pl->conf_file));
		globfree(&globbuf);
		return PAM_SUCCESS;
      	    }
	    if (retval != PAM_SUCCESS)
		goto out;
        }
    }

out:
    globfree(&globbuf);
    if (retval != PAM_SUCCESS)
    {
       	pam_syslog(pamh, LOG_WARNING, "error parsing the configuration file: '%s' ",CONF_FILE);
	return retval;
    }

    retval = setup_limits(pamh, pwd->pw_name, pwd->pw_uid, ctrl, pl);
    if (retval & LOGIN_ERR)
	pam_error(pamh, _("Too many logins for '%s'."), pwd->pw_name);
    if (retval != LIMITED_OK) {
        return PAM_PERM_DENIED;
    }

    return PAM_SUCCESS;
}
Exemplo n.º 8
0
user_config *get_user_config(pam_handle_t * pamh,
                             const module_config *cfg)
{
    _Bool non_root;
    char *kerberos_principal, *kerberos_domain;
    user_config *user_cfg = calloc(1, sizeof(user_config));

    if(!user_cfg) {
        return NULL;
    }

    if (pam_get_user(pamh, &user_cfg->username, NULL) != PAM_SUCCESS) {
        DBG(("Unable to retrieve username!"));
        free(user_cfg);
        return NULL;
    }

    DBG(("username = %s", user_cfg->username));

    non_root = strcmp(user_cfg->username, ROOT_USER);

    if (!non_root && cfg->domain != NULL) {
        kerberos_principal = extract_details(pamh, cfg->debug, "gssapi-with-mic");
        if (kerberos_principal != NULL) {
            kerberos_domain = strchr(kerberos_principal, '@');
            if (kerberos_domain != NULL && strcmp(kerberos_domain + 1, cfg->domain) == 0) {
                *kerberos_domain = '\0';
                user_cfg->username = kerberos_principal;
                user_cfg->username_allocated = 1;
                non_root = strcmp(user_cfg->username, ROOT_USER);
            } else {
              pam_syslog(pamh, LOG_ERR, "Kerberos principal does not have expected domain, ignoring : '%s'",
                         kerberos_principal);
              // cleanup char* returned by extract_details and that we do not use
              free(kerberos_principal);
            }
        }
    }

    if (cfg->ldap_enabled && non_root) {
#ifdef HAVE_LDAP
        //GET 2nd FACTORS FROM LDAP
        int rc = ldap_search_factors(pamh, cfg, user_cfg->username, user_cfg);
        if (rc < 0) {
            pam_syslog(pamh, LOG_ERR,
                       "LDAP request failed for user '%s' with error %d",
                       user_cfg->username, rc);
            free(user_cfg);
            return NULL;
        }
#else
	DBG(("LDAP configured, but not compiled (should never happen!)"));
#endif
    } else {
        //NO LDAP QUERY
        struct passwd *user_entry = NULL;
        struct pam_2fa_privs p;

        user_entry = pam_modutil_getpwnam(pamh, user_cfg->username);
        if(!user_entry) {
            pam_syslog(pamh, LOG_ERR, "Can't get passwd entry for '%s'", user_cfg->username);
            free(user_cfg);
            return NULL;
        }

#ifdef HAVE_CURL
        if(cfg->gauth_enabled && non_root) {
            strncpy(user_cfg->gauth_login, user_cfg->username, GAUTH_LOGIN_LEN + 1);
            user_cfg->gauth_login[GAUTH_LOGIN_LEN] = 0;
        }
#endif

        pam_2fa_drop_priv(pamh, &p, user_entry);
#ifdef HAVE_YKCLIENT
        yk_load_user_file(pamh, cfg, user_entry, &user_cfg->yk_publicids);
#endif
        sms_load_user_file(pamh, cfg, user_entry, user_cfg);
        pam_2fa_regain_priv(pamh, &p, user_entry);
    }

    return user_cfg;
}
Exemplo n.º 9
0
PAM_EXTERN int
pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
		     int argc, const char **argv)
{
	const void *prompt;
	const char *user;
	struct passwd *pwd;
	int ret, i, count, use_uid, debug;
	const char *left, *right, *qual;
	int quiet_fail, quiet_succ, audit;

	/* Get the user prompt. */
	ret = pam_get_item(pamh, PAM_USER_PROMPT, &prompt);
	if ((ret != PAM_SUCCESS) || (prompt == NULL) || (strlen(prompt) == 0)) {
		prompt = "login: "******"debug") == 0) {
			debug++;
		}
		if (strcmp(argv[i], "use_uid") == 0) {
			use_uid++;
		}
		if (strcmp(argv[i], "quiet") == 0) {
			quiet_fail++;
			quiet_succ++;
		}
		if (strcmp(argv[i], "quiet_fail") == 0) {
			quiet_fail++;
		}
		if (strcmp(argv[i], "quiet_success") == 0) {
			quiet_succ++;
		}
		if (strcmp(argv[i], "audit") == 0) {
			audit++;
		}
	}

	if (use_uid) {
		/* Get information about the user. */
		pwd = pam_modutil_getpwuid(pamh, getuid());
		if (pwd == NULL) {
			pam_syslog(pamh, LOG_CRIT,
				   "error retrieving information about user %lu",
				   (unsigned long)getuid());
			return PAM_USER_UNKNOWN;
		}
		user = pwd->pw_name;
	} else {
		/* Get the user's name. */
		ret = pam_get_user(pamh, &user, prompt);
		if ((ret != PAM_SUCCESS) || (user == NULL)) {
			pam_syslog(pamh, LOG_CRIT,
				   "error retrieving user name: %s",
				   pam_strerror(pamh, ret));
			return ret;
		}

		/* Get information about the user. */
		pwd = pam_modutil_getpwnam(pamh, user);
		if (pwd == NULL) {
			if(audit)
				pam_syslog(pamh, LOG_NOTICE,
					   "error retrieving information about user %s",
					   user);
			return PAM_USER_UNKNOWN;
		}
	}

	/* Walk the argument list. */
	count = 0;
	left = qual = right = NULL;
	for (i = 0; i < argc; i++) {
		if (strcmp(argv[i], "debug") == 0) {
			continue;
		}
		if (strcmp(argv[i], "use_uid") == 0) {
			continue;
		}
		if (strcmp(argv[i], "quiet") == 0) {
			continue;
		}
		if (strcmp(argv[i], "quiet_fail") == 0) {
			continue;
		}
		if (strcmp(argv[i], "quiet_success") == 0) {
			continue;
		}
		if (strcmp(argv[i], "audit") == 0) {
			continue;
		}
		if (left == NULL) {
			left = argv[i];
			continue;
		}
		if (qual == NULL) {
			qual = argv[i];
			continue;
		}
		if (right == NULL) {
			right = argv[i];
			if (right == NULL)
				continue;

			count++;
			ret = evaluate(pamh, debug,
				       left, qual, right,
				       pwd, user);
			if (ret != PAM_SUCCESS) {
				if(!quiet_fail)
					pam_syslog(pamh, LOG_INFO,
						   "requirement \"%s %s %s\" "
						   "not met by user \"%s\"",
						   left, qual, right, user);
				left = qual = right = NULL;
				break;
			}
			else
				if(!quiet_succ)
					pam_syslog(pamh, LOG_INFO,
						   "requirement \"%s %s %s\" "
						   "was met by user \"%s\"",
						   left, qual, right, user);
			left = qual = right = NULL;
			continue;
		}
	}

	if (left || qual || right) {
		ret = PAM_SERVICE_ERR;
		pam_syslog(pamh, LOG_CRIT,
			"incomplete condition detected");
	} else if (count == 0) {
		pam_syslog(pamh, LOG_INFO,
			"no condition detected; module succeeded");
	}

	return ret;
}
Exemplo n.º 10
0
/*
 * open a PAM session by making sure there's a session keyring
 */
int pam_sm_open_session(pam_handle_t *pamh, int flags UNUSED,
			int argc, const char **argv)
{
	struct passwd *pw;
	const char *username;
	int ret, old_uid, uid, old_gid, gid, loop, force = 0;

	for (loop = 0; loop < argc; loop++) {
		if (strcmp(argv[loop], "force") == 0)
			force = 1;
		else if (strcmp(argv[loop], "debug") == 0)
			xdebug = 1;
		else if (strcmp(argv[loop], "revoke") == 0)
			do_revoke = 1;
	}

	/* don't do anything if already created a keyring (will be called
	 * multiple times if mentioned more than once in a pam script)
	 */
	session_counter++;

	debug(pamh, "OPEN %d", session_counter);

	if (my_session_keyring > 0)
		return PAM_SUCCESS;

	/* look up the target UID */
	ret = pam_get_user(pamh, &username, "key user");
	if (ret != PAM_SUCCESS)
		return ret;

	pw = pam_modutil_getpwnam(pamh, username);
	if (!pw) {
		error(pamh, "Unable to look up user \"%s\"\n", username);
		return PAM_USER_UNKNOWN;
	}

	revoke_as_uid = uid = pw->pw_uid;
	old_uid = getuid();
	revoke_as_gid = gid = pw->pw_gid;
	old_gid = getgid();
	debug(pamh, "UID:%d [%d]  GID:%d [%d]", uid, old_uid, gid, old_gid);

	/* switch to the real UID and GID so that the keyring ends up owned by
	 * the right user */
	if (gid != old_gid && setregid(gid, -1) < 0) {
		error(pamh, "Unable to change GID to %d temporarily\n", gid);
		return PAM_SESSION_ERR;
	}

	if (uid != old_uid && setreuid(uid, -1) < 0) {
		error(pamh, "Unable to change UID to %d temporarily\n", uid);
		if (setregid(old_gid, -1) < 0)
			error(pamh, "Unable to change GID back to %d\n", old_gid);
		return PAM_SESSION_ERR;
	}

	ret = init_keyrings(pamh, force);

	/* return to the orignal UID and GID (probably root) */
	if (uid != old_uid && setreuid(old_uid, -1) < 0)
		ret = error(pamh, "Unable to change UID back to %d\n", old_uid);

	if (gid != old_gid && setregid(old_gid, -1) < 0)
		ret = error(pamh, "Unable to change GID back to %d\n", old_gid);

	return ret;
}
Exemplo n.º 11
0
int
save_old_password(pam_handle_t *pamh, const char *forwho, const char *oldpass,
		  int howmany)
#endif
{
    static char buf[16384];
    static char nbuf[16384];
    char *s_luser, *s_uid, *s_npas, *s_pas, *pass;
    int npas;
    FILE *pwfile, *opwfile;
    int err = 0;
    int oldmask;
    int found = 0;
    struct passwd *pwd = NULL;
    struct stat st;
#ifdef WITH_SELINUX
    security_context_t prev_context=NULL;
#endif

    if (howmany < 0) {
	return PAM_SUCCESS;
    }

    if (oldpass == NULL) {
	return PAM_SUCCESS;
    }

    oldmask = umask(077);

#ifdef WITH_SELINUX
    if (SELINUX_ENABLED) {
      security_context_t passwd_context=NULL;
      if (getfilecon("/etc/passwd",&passwd_context)<0) {
        return PAM_AUTHTOK_ERR;
      };
      if (getfscreatecon(&prev_context)<0) {
        freecon(passwd_context);
        return PAM_AUTHTOK_ERR;
      }
      if (setfscreatecon(passwd_context)) {
        freecon(passwd_context);
        freecon(prev_context);
        return PAM_AUTHTOK_ERR;
      }
      freecon(passwd_context);
    }
#endif
    pwfile = fopen(OPW_TMPFILE, "w");
    umask(oldmask);
    if (pwfile == NULL) {
      err = 1;
      goto done;
    }

    opwfile = fopen(OLD_PASSWORDS_FILE, "r");
    if (opwfile == NULL) {
	fclose(pwfile);
      err = 1;
      goto done;
    }

    if (fstat(fileno(opwfile), &st) == -1) {
	fclose(opwfile);
	fclose(pwfile);
	err = 1;
	goto done;
    }

    if (fchown(fileno(pwfile), st.st_uid, st.st_gid) == -1) {
	fclose(opwfile);
	fclose(pwfile);
	err = 1;
	goto done;
    }
    if (fchmod(fileno(pwfile), st.st_mode) == -1) {
	fclose(opwfile);
	fclose(pwfile);
	err = 1;
	goto done;
    }

    while (fgets(buf, 16380, opwfile)) {
	if (!strncmp(buf, forwho, strlen(forwho))) {
	    char *sptr = NULL;
	    found = 1;
	    if (howmany == 0)
	    	continue;
	    buf[strlen(buf) - 1] = '\0';
	    s_luser = strtok_r(buf, ":", &sptr);
	    s_uid = strtok_r(NULL, ":", &sptr);
	    s_npas = strtok_r(NULL, ":", &sptr);
	    s_pas = strtok_r(NULL, ":", &sptr);
	    npas = strtol(s_npas, NULL, 10) + 1;
	    while (npas > howmany) {
		s_pas = strpbrk(s_pas, ",");
		if (s_pas != NULL)
		    s_pas++;
		npas--;
	    }
	    pass = crypt_md5_wrapper(oldpass);
	    if (s_pas == NULL)
		snprintf(nbuf, sizeof(nbuf), "%s:%s:%d:%s\n",
			 s_luser, s_uid, npas, pass);
	    else
		snprintf(nbuf, sizeof(nbuf),"%s:%s:%d:%s,%s\n",
			 s_luser, s_uid, npas, s_pas, pass);
	    _pam_delete(pass);
	    if (fputs(nbuf, pwfile) < 0) {
		err = 1;
		break;
	    }
	} else if (fputs(buf, pwfile) < 0) {
	    err = 1;
	    break;
	}
    }
    fclose(opwfile);

    if (!found) {
	pwd = pam_modutil_getpwnam(pamh, forwho);
	if (pwd == NULL) {
	    err = 1;
	} else {
	    pass = crypt_md5_wrapper(oldpass);
	    snprintf(nbuf, sizeof(nbuf), "%s:%lu:1:%s\n",
		     forwho, (unsigned long)pwd->pw_uid, pass);
	    _pam_delete(pass);
	    if (fputs(nbuf, pwfile) < 0) {
		err = 1;
	    }
	}
    }

    if (fflush(pwfile) || fsync(fileno(pwfile))) {
	D(("fflush or fsync error writing entries to old passwords file: %m"));
	err = 1;
    }

    if (fclose(pwfile)) {
	D(("fclose error writing entries to old passwords file: %m"));
	err = 1;
    }

done:
    if (!err) {
	if (rename(OPW_TMPFILE, OLD_PASSWORDS_FILE))
	    err = 1;
    }
#ifdef WITH_SELINUX
    if (SELINUX_ENABLED) {
      if (setfscreatecon(prev_context)) {
        err = 1;
      }
      if (prev_context)
        freecon(prev_context);
      prev_context=NULL;
    }
#endif
    if (!err) {
	return PAM_SUCCESS;
    } else {
	unlink(OPW_TMPFILE);
	return PAM_AUTHTOK_ERR;
    }
}
Exemplo n.º 12
0
int
pam_sm_chauthtok (pam_handle_t *pamh, int flags, int argc, const char **argv)
{
  struct passwd *pwd;
  const char *newpass;
  const char *user;
    int retval, tries;
  options_t options;

  memset (&options, 0, sizeof (options));

  /* Set some default values, which could be overwritten later.  */
  options.remember = 10;
  options.tries = 1;

  /* Parse parameters for module */
  for ( ; argc-- > 0; argv++)
    parse_option (pamh, *argv, &options);

  if (options.debug)
    pam_syslog (pamh, LOG_DEBUG, "pam_sm_chauthtok entered");


  if (options.remember == 0)
    return PAM_IGNORE;

  retval = pam_get_user (pamh, &user, NULL);
  if (retval != PAM_SUCCESS)
    return retval;

  if (user == NULL || strlen (user) == 0)
    {
      if (options.debug)
	pam_syslog (pamh, LOG_DEBUG,
		    "User is not known to system");

      return PAM_USER_UNKNOWN;
    }

  if (flags & PAM_PRELIM_CHECK)
    {
      if (options.debug)
	pam_syslog (pamh, LOG_DEBUG,
		    "pam_sm_chauthtok(PAM_PRELIM_CHECK)");

      return PAM_SUCCESS;
    }

  pwd = pam_modutil_getpwnam (pamh, user);
  if (pwd == NULL)
    return PAM_USER_UNKNOWN;

  if ((strcmp(pwd->pw_passwd, "x") == 0)  ||
      ((pwd->pw_passwd[0] == '#') &&
       (pwd->pw_passwd[1] == '#') &&
       (strcmp(pwd->pw_name, pwd->pw_passwd + 2) == 0)))
    {
      struct spwd *spw = pam_modutil_getspnam (pamh, user);
      if (spw == NULL)
	return PAM_USER_UNKNOWN;

      retval = save_old_pass (pamh, user, pwd->pw_uid, spw->sp_pwdp,
			      options.remember, options.debug);
      if (retval != PAM_SUCCESS)
	return retval;
    }
  else
    {
      retval = save_old_pass (pamh, user, pwd->pw_uid, pwd->pw_passwd,
			      options.remember, options.debug);
      if (retval != PAM_SUCCESS)
	return retval;
    }

  newpass = NULL;
  tries = 0;
  while ((newpass == NULL) && (tries < options.tries))
    {
      retval = pam_get_authtok (pamh, PAM_AUTHTOK, &newpass, NULL);
      if (retval != PAM_SUCCESS && retval != PAM_TRY_AGAIN)
	{
	  if (retval == PAM_CONV_AGAIN)
	    retval = PAM_INCOMPLETE;
	  return retval;
	}
      tries++;

      if (options.debug)
	{
	  if (newpass)
	    pam_syslog (pamh, LOG_DEBUG, "got new auth token");
	  else
	    pam_syslog (pamh, LOG_DEBUG, "got no auth token");
	}

      if (newpass == NULL || retval == PAM_TRY_AGAIN)
	continue;

      if (options.debug)
	pam_syslog (pamh, LOG_DEBUG, "check against old password file");

      if (check_old_pass (pamh, user, newpass,
			  options.debug) != PAM_SUCCESS)
	{
	  if (getuid() || options.enforce_for_root ||
	      (flags & PAM_CHANGE_EXPIRED_AUTHTOK))
	    {
	      pam_error (pamh,
		         _("Password has been already used. Choose another."));
	      newpass = NULL;
	      /* Remove password item, else following module will use it */
	      pam_set_item (pamh, PAM_AUTHTOK, (void *) NULL);
	    }
	  else
	    pam_info (pamh,
		       _("Password has been already used."));
	}
    }

  if (newpass == NULL && tries >= options.tries)
    {
      if (options.debug)
	pam_syslog (pamh, LOG_DEBUG, "Aborted, too many tries");
      return PAM_MAXTRIES;
    }

  return PAM_SUCCESS;
}
Exemplo n.º 13
0
PAM_EXTERN int
pam_sm_authenticate (pam_handle_t *pamh, int flags UNUSED,
		     int argc, const char **argv)
{
    int retval, i, citem=0, extitem=0, onerr=PAM_SERVICE_ERR, sense=2, quiet=0;
    const void *void_citemp;
    const char *citemp;
    char *ifname=NULL;
    char aline[256];
    char mybuf[256],myval[256];
    struct stat fileinfo;
    FILE *inf;
    char apply_val[256];
    int apply_type;

    /* Stuff for "extended" items */
    struct passwd *userinfo;

    apply_type=APPLY_TYPE_NULL;
    memset(apply_val,0,sizeof(apply_val));

    for(i=0; i < argc; i++) {
	{
	    const char *junk;

	    /* option quiet has no value */
	    if(!strcmp(argv[i],"quiet")) {
		quiet = 1;
		continue;
	    }

	    memset(mybuf,'\0',sizeof(mybuf));
	    memset(myval,'\0',sizeof(myval));
	    junk = strchr(argv[i], '=');
	    if((junk == NULL) || (junk - argv[i]) >= (int) sizeof(mybuf)) {
		pam_syslog(pamh,LOG_ERR, "Bad option: \"%s\"",
			 argv[i]);
		continue;
	    }
	    strncpy(mybuf, argv[i],
		    LESSER(junk - argv[i], (int)sizeof(mybuf) - 1));
	    strncpy(myval, junk + 1, sizeof(myval) - 1);
	}
	if(!strcmp(mybuf,"onerr"))
	    if(!strcmp(myval,"succeed"))
		onerr = PAM_SUCCESS;
	    else if(!strcmp(myval,"fail"))
		onerr = PAM_SERVICE_ERR;
	    else {
	        if (ifname) free (ifname);
		return PAM_SERVICE_ERR;
	    }
	else if(!strcmp(mybuf,"sense"))
	    if(!strcmp(myval,"allow"))
		sense=0;
	    else if(!strcmp(myval,"deny"))
		sense=1;
	    else {
	        if (ifname) free (ifname);
		return onerr;
	    }
	else if(!strcmp(mybuf,"file")) {
	    if (ifname) free (ifname);
	    ifname = (char *)malloc(strlen(myval)+1);
	    if (!ifname)
		return PAM_BUF_ERR;
	    strcpy(ifname,myval);
	} else if(!strcmp(mybuf,"item"))
	    if(!strcmp(myval,"user"))
		citem = PAM_USER;
	    else if(!strcmp(myval,"tty"))
		citem = PAM_TTY;
	    else if(!strcmp(myval,"rhost"))
		citem = PAM_RHOST;
	    else if(!strcmp(myval,"ruser"))
		citem = PAM_RUSER;
	    else { /* These items are related to the user, but are not
		      directly gettable with pam_get_item */
		citem = PAM_USER;
		if(!strcmp(myval,"group"))
		    extitem = EI_GROUP;
		else if(!strcmp(myval,"shell"))
		    extitem = EI_SHELL;
		else
		    citem = 0;
	    } else if(!strcmp(mybuf,"apply")) {
		apply_type=APPLY_TYPE_NONE;
		memset(apply_val,'\0',sizeof(apply_val));
		if (myval[0]=='@') {
		    apply_type=APPLY_TYPE_GROUP;
		    strncpy(apply_val,myval+1,sizeof(apply_val)-1);
		} else {
		    apply_type=APPLY_TYPE_USER;
		    strncpy(apply_val,myval,sizeof(apply_val)-1);
		}
	    } else {
		free(ifname);
		pam_syslog(pamh,LOG_ERR, "Unknown option: %s",mybuf);
		return onerr;
	    }
    }

    if(!citem) {
	pam_syslog(pamh,LOG_ERR,
		  "Unknown item or item not specified");
	free(ifname);
	return onerr;
    } else if(!ifname) {
	pam_syslog(pamh,LOG_ERR, "List filename not specified");
	return onerr;
    } else if(sense == 2) {
	pam_syslog(pamh,LOG_ERR,
		  "Unknown sense or sense not specified");
	free(ifname);
	return onerr;
    } else if(
	      (apply_type==APPLY_TYPE_NONE) ||
	      ((apply_type!=APPLY_TYPE_NULL) && (*apply_val=='\0'))
              ) {
	pam_syslog(pamh,LOG_ERR,
		  "Invalid usage for apply= parameter");
        free (ifname);
	return onerr;
    }

    /* Check if it makes sense to use the apply= parameter */
    if (apply_type != APPLY_TYPE_NULL) {
	if((citem==PAM_USER) || (citem==PAM_RUSER)) {
	    pam_syslog(pamh,LOG_WARNING,
		      "Non-sense use for apply= parameter");
	    apply_type=APPLY_TYPE_NULL;
	}
	if(extitem && (extitem==EI_GROUP)) {
	    pam_syslog(pamh,LOG_WARNING,
		      "Non-sense use for apply= parameter");
	    apply_type=APPLY_TYPE_NULL;
	}
    }

    /* Short-circuit - test if this session apply for this user */
    {
	const char *user_name;
	int rval;

	rval=pam_get_user(pamh,&user_name,NULL);
	if((rval==PAM_SUCCESS) && user_name && user_name[0]) {
	    /* Got it ? Valid ? */
	    if(apply_type==APPLY_TYPE_USER) {
		if(strcmp(user_name, apply_val)) {
		    /* Does not apply to this user */
#ifdef PAM_DEBUG
		    pam_syslog(pamh,LOG_DEBUG,
			      "don't apply: apply=%s, user=%s",
			     apply_val,user_name);
#endif /* PAM_DEBUG */
		    free(ifname);
		    return PAM_IGNORE;
		}
	    } else if(apply_type==APPLY_TYPE_GROUP) {
		if(!pam_modutil_user_in_group_nam_nam(pamh,user_name,apply_val)) {
		    /* Not a member of apply= group */
#ifdef PAM_DEBUG
		    pam_syslog(pamh,LOG_DEBUG,

			     "don't apply: %s not a member of group %s",
			     user_name,apply_val);
#endif /* PAM_DEBUG */
		    free(ifname);
		    return PAM_IGNORE;
		}
	    }
	}
    }

    retval = pam_get_item(pamh,citem,&void_citemp);
    citemp = void_citemp;
    if(retval != PAM_SUCCESS) {
	free(ifname);
	return onerr;
    }
    if((citem == PAM_USER) && !citemp) {
	retval = pam_get_user(pamh,&citemp,NULL);
	if (retval != PAM_SUCCESS || !citemp) {
	    free(ifname);
	    return PAM_SERVICE_ERR;
	}
    }
    if((citem == PAM_TTY) && citemp) {
        /* Normalize the TTY name. */
        if(strncmp(citemp, "/dev/", 5) == 0) {
            citemp += 5;
        }
    }

    if(!citemp || (strlen(citemp) == 0)) {
	free(ifname);
	/* The item was NULL - we are sure not to match */
	return sense?PAM_SUCCESS:PAM_AUTH_ERR;
    }

    if(extitem) {
	switch(extitem) {
	    case EI_GROUP:
		/* Just ignore, call pam_modutil_in_group... later */
		break;
	    case EI_SHELL:
		/* Assume that we have already gotten PAM_USER in
		   pam_get_item() - a valid assumption since citem
		   gets set to PAM_USER in the extitem switch */
		userinfo = pam_modutil_getpwnam(pamh, citemp);
		if (userinfo == NULL) {
		    pam_syslog(pamh,LOG_ERR, "getpwnam(%s) failed",
			     citemp);
		    free(ifname);
		    return onerr;
		}
		citemp = userinfo->pw_shell;
		break;
	    default:
		pam_syslog(pamh,LOG_ERR,

			 "Internal weirdness, unknown extended item %d",
			 extitem);
		free(ifname);
		return onerr;
	}
    }
#ifdef PAM_DEBUG
    pam_syslog(pamh,LOG_INFO,

	     "Got file = %s, item = %d, value = %s, sense = %d",
	     ifname, citem, citemp, sense);
#endif
    if(lstat(ifname,&fileinfo)) {
	if(!quiet)
		pam_syslog(pamh,LOG_ERR, "Couldn't open %s",ifname);
	free(ifname);
	return onerr;
    }

    if((fileinfo.st_mode & S_IWOTH)
       || !S_ISREG(fileinfo.st_mode)) {
	/* If the file is world writable or is not a
	   normal file, return error */
	pam_syslog(pamh,LOG_ERR,
		 "%s is either world writable or not a normal file",
		 ifname);
	free(ifname);
	return PAM_AUTH_ERR;
    }

    inf = fopen(ifname,"r");
    if(inf == NULL) { /* Check that we opened it successfully */
	if (onerr == PAM_SERVICE_ERR) {
	    /* Only report if it's an error... */
	    pam_syslog(pamh,LOG_ERR,  "Error opening %s", ifname);
	}
	free(ifname);
	return onerr;
    }
    /* There should be no more errors from here on */
    retval=PAM_AUTH_ERR;
    /* This loop assumes that PAM_SUCCESS == 0
       and PAM_AUTH_ERR != 0 */
#ifdef PAM_DEBUG
    assert(PAM_SUCCESS == 0);
    assert(PAM_AUTH_ERR != 0);
#endif
    while((fgets(aline,sizeof(aline),inf) != NULL)
	  && retval) {
	char *a = aline;

	if(strlen(aline) == 0)
	    continue;
	if(aline[strlen(aline) - 1] == '\n')
	    aline[strlen(aline) - 1] = '\0';
	if(strlen(aline) == 0)
	    continue;
	if(aline[strlen(aline) - 1] == '\r')
	    aline[strlen(aline) - 1] = '\0';
	if(citem == PAM_TTY) {
	    if(strncmp(a, "/dev/", 5) == 0)
		a += 5;
	}
	if (extitem == EI_GROUP) {
	    retval = !pam_modutil_user_in_group_nam_nam(pamh,
		citemp, aline);
	} else {
	    retval = strcmp(a, citemp);
	}
    }

    fclose(inf);
    free(ifname);
    if ((sense && retval) || (!sense && !retval)) {
#ifdef PAM_DEBUG
	pam_syslog(pamh,LOG_INFO,
		 "Returning PAM_SUCCESS, retval = %d", retval);
#endif
	return PAM_SUCCESS;
    }
    else {
	const void *service;
	const char *user_name;
#ifdef PAM_DEBUG
	pam_syslog(pamh,LOG_INFO,
		 "Returning PAM_AUTH_ERR, retval = %d", retval);
#endif
	(void) pam_get_item(pamh, PAM_SERVICE, &service);
	(void) pam_get_user(pamh, &user_name, NULL);
	if (!quiet)
	    pam_syslog (pamh, LOG_ALERT, "Refused user %s for service %s",
	                user_name, (const char *)service);
	return PAM_AUTH_ERR;
    }
}
Exemplo n.º 14
0
/* --- authentication (locking out inactive users) functions --- */
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags,
		    int argc, const char **argv)
{
    int retval, ctrl;
    const char *user = NULL;
    const struct passwd *pwd;
    uid_t uid;
    time_t lltime = 0;
    time_t inactive_days = 0;
    int last_fd;

    /*
     * Lock out the user if he did not login recently enough.
     */

    ctrl = _pam_auth_parse(pamh, flags, argc, argv, &inactive_days);

    /* which user? */

    if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS || user == NULL
        || *user == '\0') {
        pam_syslog(pamh, LOG_ERR, "cannot determine the user's name");
        return PAM_USER_UNKNOWN;
    }

    /* what uid? */

    pwd = pam_modutil_getpwnam (pamh, user);
    if (pwd == NULL) {
        pam_syslog(pamh, LOG_ERR, "user unknown");
	return PAM_USER_UNKNOWN;
    }
    uid = pwd->pw_uid;
    pwd = NULL;                                         /* tidy up */

    if (uid == 0)
	return PAM_SUCCESS;

    /* obtain the last login date and all the relevant info */
    last_fd = last_login_open(pamh, ctrl, uid);
    if (last_fd < 0) {
	return PAM_IGNORE;
    }

    retval = last_login_read(pamh, ctrl|LASTLOG_QUIET, last_fd, uid, &lltime);
    close(last_fd);

    if (retval != PAM_SUCCESS) {
	D(("error while reading lastlog file"));
	return PAM_IGNORE;
    }

    if (lltime == 0) { /* user never logged in before */
        if (ctrl & LASTLOG_DEBUG)
            pam_syslog(pamh, LOG_DEBUG, "user never logged in - pass");
        return PAM_SUCCESS;
    }

    lltime = (time(NULL) - lltime) / (24*60*60);

    if (lltime > inactive_days) {
        pam_syslog(pamh, LOG_INFO, "user %s inactive for %ld days - denied",
		   user, (long) lltime);
        return PAM_AUTH_ERR;
    }

    return PAM_SUCCESS;
}
Exemplo n.º 15
0
/*
 * Verify the user authorization.  Call krb5_kuserok if this is a local
 * account, or do the krb5_aname_to_localname verification if ignore_k5login
 * was requested.  For non-local accounts, the principal must match the
 * authentication identity.
 */
int
pamk5_authorized(struct pam_args *args)
{
    struct context *ctx;
    krb5_context c;
    struct passwd *pwd;
    char kuser[65];             /* MAX_USERNAME == 65 (MIT Kerberos 1.4.1). */

    if (args == NULL || args->ctx == NULL || args->ctx->context == NULL)
        return PAM_SERVICE_ERR;
    ctx = args->ctx;
    if (ctx->name == NULL)
        return PAM_SERVICE_ERR;
    c = ctx->context;

    /*
     * If alt_auth_map was set, authorize the user if the authenticated
     * principal matches the mapped principal.  alt_auth_map essentially
     * serves as a supplemental .k5login.
     */
    if (args->alt_auth_map != NULL) {
        char *mapped;
        char *authed;
        int retval;
        krb5_principal princ;

        if (pamk5_map_principal(args, ctx->name, &mapped) != PAM_SUCCESS)
            return PAM_SERVICE_ERR;
        retval = krb5_parse_name(c, mapped, &princ);
        if (retval != 0) {
            free(mapped);
            return PAM_SERVICE_ERR;
        }
        free(mapped);
        retval = krb5_unparse_name(c, princ, &mapped);
        if (retval != 0)
            return PAM_SERVICE_ERR;
        retval = krb5_unparse_name(c, ctx->princ, &authed);
        if (retval != 0) {
            free(mapped);
            return PAM_SERVICE_ERR;
        }
        if (strcmp(authed, mapped) == 0) {
            free(authed);
            free(mapped);
            return PAM_SUCCESS;
        }
        free(authed);
        free(mapped);
    }

    /*
     * If the name to which we're authenticating contains @ (is fully
     * qualified), it must match the principal exactly.
     */
    if (strchr(ctx->name, '@') != NULL) {
        char *principal;
        int retval;

        retval = krb5_unparse_name(c, ctx->princ, &principal);
        if (retval != 0)
            return PAM_SERVICE_ERR;
        if (strcmp(principal, ctx->name) != 0) {
            free(principal);
            return PAM_AUTH_ERR;
        }
        return PAM_SUCCESS;
    }

    /*
     * Otherwise, apply either krb5_aname_to_localname or krb5_kuserok
     * depending on the situation.
     */
    pwd = pam_modutil_getpwnam(args->pamh, ctx->name);
    if (args->ignore_k5login || pwd == NULL) {
        if (krb5_aname_to_localname(c, ctx->princ, sizeof(kuser), kuser) != 0)
            return PAM_AUTH_ERR;
        if (strcmp(kuser, ctx->name) != 0)
            return PAM_AUTH_ERR;
    } else {
        if (!krb5_kuserok(c, ctx->princ, ctx->name))
            return PAM_AUTH_ERR;
    }

    return PAM_SUCCESS;
}
Exemplo n.º 16
0
static int
get_folder(pam_handle_t *pamh, int ctrl,
	   const char *path_mail, char **folder_p, size_t hashcount)
{
    int retval;
    const char *user, *path;
    char *folder = NULL;
    const struct passwd *pwd = NULL;

    retval = pam_get_user(pamh, &user, NULL);
    if (retval != PAM_SUCCESS || user == NULL) {
	pam_syslog(pamh, LOG_ERR, "cannot determine username");
	retval = PAM_USER_UNKNOWN;
	goto get_folder_cleanup;
    }

    if (ctrl & PAM_NEW_MAIL_DIR) {
	path = path_mail;
	if (*path == '~') {	/* support for $HOME delivery */
	    pwd = pam_modutil_getpwnam(pamh, user);
	    if (pwd == NULL) {
		pam_syslog(pamh, LOG_ERR, "user unknown");
		retval = PAM_USER_UNKNOWN;
		goto get_folder_cleanup;
	    }
	    /*
	     * "~/xxx" and "~xxx" are treated as same
	     */
	    if (!*++path || (*path == '/' && !*++path)) {
		pam_syslog(pamh, LOG_ERR,
			   "badly formed mail path [%s]", path_mail);
		retval = PAM_SERVICE_ERR;
		goto get_folder_cleanup;
	    }
	    ctrl |= PAM_HOME_MAIL;
	    if (hashcount != 0) {
		pam_syslog(pamh, LOG_ERR,
			   "cannot do hash= and home directory mail");
	    }
	}
    } else {
	path = DEFAULT_MAIL_DIRECTORY;
    }

    /* put folder together */

    hashcount = hashcount < strlen(user) ? hashcount : strlen(user);

    retval = PAM_BUF_ERR;
    if (ctrl & PAM_HOME_MAIL) {
	if (asprintf(&folder, MAIL_FILE_FORMAT, pwd->pw_dir, "", path) < 0)
	    goto get_folder_cleanup;
    } else {
	int rc;
	size_t i;
	char *hash;

	if ((hash = malloc(2 * hashcount + 1)) == NULL)
	    goto get_folder_cleanup;

	for (i = 0; i < hashcount; i++) {
	    hash[2 * i] = '/';
	    hash[2 * i + 1] = user[i];
	}
	hash[2 * i] = '\0';

	rc = asprintf(&folder, MAIL_FILE_FORMAT, path, hash, user);
	_pam_overwrite(hash);
	_pam_drop(hash);
	if (rc < 0)
	    goto get_folder_cleanup;
    }
    D(("folder=[%s]", folder));
    retval = PAM_SUCCESS;

    /* tidy up */

  get_folder_cleanup:
    user = NULL;
    path = NULL;

    *folder_p = folder;
    folder = NULL;

    if (retval == PAM_BUF_ERR)
	pam_syslog(pamh, LOG_CRIT, "out of memory for mail folder");

    return retval;
}