[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Login PAM interaction suspect




I'm not sure where to discuss this, but this seemed like a venue that would attract some knowledgeable feedback.

I am designing a PAM module to serve as a backup authentication mechanism for a device when it has lost network connectivity to it's LDAP server. There is no local password containing file on the system. The credential used has OTP-ish properties and encodes a privilege level as well. Upon successful authentication, I planned on the module fabricating a one line passwd file that would be timestamped and deleted past usage. A helper nsslib function will deal with fronting the "user" information to login and the system.

I was wondering which api service to put the logic the builds the authenticated user information.
 I was thinking that pam_setcred() would be appropriate.

But then I decided to read the login code and my head exploded. Below is a simplified snippet of the login code we have in our distro. I looked at the current code on kernel.org, and it seems to be rewritten and refactored into multiple subroutines, but still has the same logic flow.

---<snippet of login.c>----
    /*
     * Grab the user information out of the password file for future usage
     * First get the username that we are actually using, though.
     */
    retcode = pam_get_item(pamh, PAM_USER, (const void **) &username);
    PAM_FAIL_CHECK;

    if (!username || !*username) {
	    fprintf(stderr, _("\nSession setup problem, abort.\n"));
	    syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),
		   __FUNCTION__, __LINE__);
	    pam_end(pamh, PAM_SYSTEM_ERR);
	    exit(1);
    }
    if (!(pwd = getpwnam(username))) {
	    fprintf(stderr, _("\nSession setup problem, abort.\n"));
	    syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),
		   username, __FUNCTION__, __LINE__);
	    pam_end(pamh, PAM_SYSTEM_ERR);
	    exit(1);
    }

    /*
     * Create a copy of the pwd struct - otherwise it may get
     * clobbered by PAM
     */
    memcpy(&pwdcopy, pwd, sizeof(*pwd));
    pwd = &pwdcopy;
    pwd->pw_name   = strdup(pwd->pw_name);
    pwd->pw_passwd = strdup(pwd->pw_passwd);
    pwd->pw_gecos  = strdup(pwd->pw_gecos);
    pwd->pw_dir    = strdup(pwd->pw_dir);
    pwd->pw_shell  = strdup(pwd->pw_shell);
    if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos ||
	!pwd->pw_dir || !pwd->pw_shell) {
	    fprintf(stderr, _("login: Out of memory\n"));
	    syslog(LOG_ERR, "Out of memory");
	    pam_end(pamh, PAM_SYSTEM_ERR);
	    exit(1);
    }
    username = pwd->pw_name;

    /*
     * Initialize the supplementary group list.
     * This should be done before pam_setcred because
     * the PAM modules might add groups during pam_setcred.
     */
    if (initgroups(username, pwd->pw_gid) < 0) {
	    syslog(LOG_ERR, "initgroups: %m");
	    fprintf(stderr, _("\nSession setup problem, abort.\n"));
	    pam_end(pamh, PAM_SYSTEM_ERR);
	    exit(1);
    }

    retcode = pam_open_session(pamh, 0);
    PAM_FAIL_CHECK;

    retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);
    PAM_FAIL_CHECK;

	----<end snippet>----

1) Notice that pam_setcred is called _after_  pam_open_session.
This is direct conflict with the documentation of pam_setcred and doesn't bode well for any session parameters that might have been created using context created by setcred.

2) The bigger picture: notice that getpwnam() and initgroups() has been called before any of this. So if pam_setcred() or pam_open_session() have any effect on PAM_USER, the user database, or the information returned by nss functions they will not be noticed by login.

[the comment about PAM clobblering the pwd info from getpwnam() is misleading. PAM has no access to data in the program except though interfaces. But getpwnam() does return a pointer to an internal static structure, so copying the information out right away is just good system interface practice.]

Well I can figure out how to work around this, but this just seems wrong. The PAM interfacing should be completed (setcred, open_session), before fetching any info on the user. This probably makes no difference for LDAP users, if the fetched information was cached and available to nss after authentication time, but screws up anyone building something slightly more complicated. Perhaps those that work with this more can see some other issues or explain.

I think, that the calls to PAM should be moved to the beginning of this snippet, and put in proper order. ... but I'm new in this space.

   David Mitton.


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]