Exemple #1
0
static int knet_vty_pam_auth_user(struct knet_vty *vty, const char *user)
{
	pam_handle_t *pamh=NULL;
	struct pam_conv conv;
	int err;
	int retry = 1;

	conv.conv = knet_pam_misc_conv;
	conv.appdata_ptr = (void *)vty;

retry_auth:
	err = pam_start("kronosnet", user, &conv, &pamh);
	if (err != PAM_SUCCESS) {
		errno = EINVAL;
		log_error("PAM fatal error: %s", pam_strerror(pamh, err));
		knet_vty_write(vty, "PAM fatal error: %s",
				pam_strerror(pamh, err));
		goto out_fatal;
	}

	err = pam_authenticate(pamh, 0);
	if (err != PAM_SUCCESS) {
		if (vty->got_epipe) {
			errno = EPIPE;
			goto out_fatal;
		} else {
			errno = EINVAL;
			goto out_clean;
		}
	}

	if (knet_vty_get_pam_user(vty, pamh) != PAM_SUCCESS) {
		log_error("PAM: unable to get PAM_USER: %s",
			  pam_strerror(pamh, err));
		knet_vty_write(vty, "PAM: unable to get PAM_USER: %s",
				pam_strerror(pamh, err));
		goto out_clean;
	}

	err = pam_acct_mgmt(pamh, 0);
	if (err != PAM_SUCCESS) {
		log_info("User: %s failed to authenticate on vty(%d) attempt %d",
			 vty->username, vty->conn_num, retry);
		goto out_clean;
	}

out_clean:
	if (pamh) {
		pam_end(pamh, err);
		pamh = NULL;
	}

	if ((err != PAM_SUCCESS) && (retry < AUTH_MAX_RETRY)) {
		retry++;
		goto retry_auth;
	}

out_fatal:
	if (pamh) {
		pam_end(pamh, err);
		pamh = NULL;
	}

	knet_vty_write(vty, "\n");

	return err;
}
Exemple #2
0
int
authenticate_user(const char *user, const char *passwd)
{
#ifndef HAVE_PAM
    gboolean pass = TRUE;
#else
    int rc = 0;
    gboolean pass = FALSE;
    const void *p_user = NULL;

    struct pam_conv p_conv;
    struct pam_handle *pam_h = NULL;
    static const char *pam_name = NULL;

    if (pam_name == NULL) {
        pam_name = getenv("CIB_pam_service");
    }
    if (pam_name == NULL) {
        pam_name = "login";
    }

    p_conv.conv = construct_pam_passwd;
    p_conv.appdata_ptr = strdup(passwd);

    rc = pam_start(pam_name, user, &p_conv, &pam_h);
    if (rc != PAM_SUCCESS) {
        crm_err("Could not initialize PAM: %s (%d)", pam_strerror(pam_h, rc), rc);
        goto bail;
    }

    rc = pam_authenticate(pam_h, 0);
    if (rc != PAM_SUCCESS) {
        crm_err("Authentication failed for %s: %s (%d)", user, pam_strerror(pam_h, rc), rc);
        goto bail;
    }

    /* Make sure we authenticated the user we wanted to authenticate.
     * Since we also run as non-root, it might be worth pre-checking
     * the user has the same EID as us, since that the only user we
     * can authenticate.
     */
    rc = pam_get_item(pam_h, PAM_USER, &p_user);
    if (rc != PAM_SUCCESS) {
        crm_err("Internal PAM error: %s (%d)", pam_strerror(pam_h, rc), rc);
        goto bail;

    } else if (p_user == NULL) {
        crm_err("Unknown user authenticated.");
        goto bail;

    } else if (safe_str_neq(p_user, user)) {
        crm_err("User mismatch: %s vs. %s.", (const char *)p_user, (const char *)user);
        goto bail;
    }

    rc = pam_acct_mgmt(pam_h, 0);
    if (rc != PAM_SUCCESS) {
        crm_err("Access denied: %s (%d)", pam_strerror(pam_h, rc), rc);
        goto bail;
    }
    pass = TRUE;

  bail:
    pam_end(pam_h, rc);
#endif
    return pass;
}
static void
create_watching_parent (void)
{
    pid_t child;
    sigset_t ourset;
    struct sigaction oldact[3];
    int status = 0;
    int retval;

    retval = pam_open_session (pamh, 0);
    if (is_pam_failure(retval))
    {
        cleanup_pam (retval);
        errx (EXIT_FAILURE, _("cannot open session: %s"),
              pam_strerror (pamh, retval));
    }
    else
        _pam_session_opened = 1;

    memset(oldact, 0, sizeof(oldact));

    child = fork ();
    if (child == (pid_t) -1)
    {
        cleanup_pam (PAM_ABORT);
        err (EXIT_FAILURE, _("cannot create child process"));
    }

    /* the child proceeds to run the shell */
    if (child == 0)
        return;

    /* In the parent watch the child.  */

    /* su without pam support does not have a helper that keeps
       sitting on any directory so let's go to /.  */
    if (chdir ("/") != 0)
        warn (_("cannot change directory to %s"), "/");

    sigfillset (&ourset);
    if (sigprocmask (SIG_BLOCK, &ourset, NULL))
    {
        warn (_("cannot block signals"));
        caught_signal = true;
    }
    if (!caught_signal)
    {
        struct sigaction action;
        action.sa_handler = su_catch_sig;
        sigemptyset (&action.sa_mask);
        action.sa_flags = 0;
        sigemptyset (&ourset);
        if (!same_session)
        {
            if (sigaddset(&ourset, SIGINT) || sigaddset(&ourset, SIGQUIT))
            {
                warn (_("cannot set signal handler"));
                caught_signal = true;
            }
        }
        if (!caught_signal && (sigaddset(&ourset, SIGTERM)
                               || sigaddset(&ourset, SIGALRM)
                               || sigaction(SIGTERM, &action, &oldact[0])
                               || sigprocmask(SIG_UNBLOCK, &ourset, NULL))) {
            warn (_("cannot set signal handler"));
            caught_signal = true;
        }
        if (!caught_signal && !same_session && (sigaction(SIGINT, &action, &oldact[1])
                                                || sigaction(SIGQUIT, &action, &oldact[2])))
        {
            warn (_("cannot set signal handler"));
            caught_signal = true;
        }
    }
    if (!caught_signal)
    {
        pid_t pid;
        for (;;)
        {
            pid = waitpid (child, &status, WUNTRACED);

            if (pid != (pid_t)-1 && WIFSTOPPED (status))
            {
                kill (getpid (), SIGSTOP);
                /* once we get here, we must have resumed */
                kill (pid, SIGCONT);
            }
            else
                break;
        }
        if (pid != (pid_t)-1)
        {
            if (WIFSIGNALED (status))
            {
                fprintf (stderr, "%s%s\n", strsignal (WTERMSIG (status)),
                         WCOREDUMP (status) ? _(" (core dumped)") : "");
                status = WTERMSIG (status) + 128;
            }
            else
                status = WEXITSTATUS (status);
        }
        else if (caught_signal)
            status = caught_signal + 128;
        else
            status = 1;
    }
    else
        status = 1;

    if (caught_signal)
    {
        fprintf (stderr, _("\nSession terminated, killing shell..."));
        kill (child, SIGTERM);
    }

    cleanup_pam (PAM_SUCCESS);

    if (caught_signal)
    {
        sleep (2);
        kill (child, SIGKILL);
        fprintf (stderr, _(" ...killed.\n"));

        /* Let's terminate itself with the received signal.
         *
         * It seems that shells use WIFSIGNALED() rather than our exit status
         * value to detect situations when is necessary to cleanup (reset)
         * terminal settings (kzak -- Jun 2013).
         */
        switch (caught_signal) {
        case SIGTERM:
            sigaction(SIGTERM, &oldact[0], NULL);
            break;
        case SIGINT:
            sigaction(SIGINT, &oldact[1], NULL);
            break;
        case SIGQUIT:
            sigaction(SIGQUIT, &oldact[2], NULL);
            break;
        default:
            /* just in case that signal stuff initialization failed and
             * caught_signal = true */
            caught_signal = SIGKILL;
            break;
        }
        kill(getpid(), caught_signal);
    }
    exit (status);
}
Exemple #4
0
static void loginpam_auth(struct login_context *cxt)
{
	int rc, failcount = 0, show_unknown, retries;
	const char *hostname = cxt->hostname ? cxt->hostname :
			       cxt->tty_name ? cxt->tty_name : "<unknown>";
	pam_handle_t *pamh = cxt->pamh;

	/* if we didn't get a user on the command line, set it to NULL */
	loginpam_get_username(pamh, &cxt->username);

	show_unknown = getlogindefs_bool("LOG_UNKFAIL_ENAB", 0);
	retries = getlogindefs_num("LOGIN_RETRIES", LOGIN_MAX_TRIES);

	/*
	 * There may be better ways to deal with some of these conditions, but
	 * at least this way I don't think we'll be giving away information...
	 *
	 * Perhaps someday we can trust that all PAM modules will pay attention
	 * to failure count and get rid of LOGIN_MAX_TRIES?
	 */
	rc = pam_authenticate(pamh, 0);

	while ((++failcount < retries) &&
	       ((rc == PAM_AUTH_ERR) ||
		(rc == PAM_USER_UNKNOWN) ||
		(rc == PAM_CRED_INSUFFICIENT) ||
		(rc == PAM_AUTHINFO_UNAVAIL))) {

		if (rc == PAM_USER_UNKNOWN && !show_unknown)
			/*
			 * logging unknown usernames may be a security issue if
			 * an user enter her password instead of her login name
			 */
			cxt->username = NULL;
		else
			loginpam_get_username(pamh, &cxt->username);

		syslog(LOG_NOTICE,
		       _("FAILED LOGIN %d FROM %s FOR %s, %s"),
		       failcount, hostname,
		       cxt->username ? cxt->username : "******",
		       pam_strerror(pamh, rc));

		log_btmp(cxt);
		log_audit(cxt, 0);

		fprintf(stderr, _("Login incorrect\n\n"));

		pam_set_item(pamh, PAM_USER, NULL);
		rc = pam_authenticate(pamh, 0);
	}

	if (is_pam_failure(rc)) {

		if (rc == PAM_USER_UNKNOWN && !show_unknown)
			cxt->username = NULL;
		else
			loginpam_get_username(pamh, &cxt->username);

		if (rc == PAM_MAXTRIES)
			syslog(LOG_NOTICE,
			       _("TOO MANY LOGIN TRIES (%d) FROM %s FOR %s, %s"),
			       failcount, hostname,
			       cxt->username ? cxt->username : "******",
			       pam_strerror(pamh, rc));
		else
			syslog(LOG_NOTICE,
			       _("FAILED LOGIN SESSION FROM %s FOR %s, %s"),
			       hostname,
			       cxt->username ? cxt->username : "******",
			       pam_strerror(pamh, rc));

		log_btmp(cxt);
		log_audit(cxt, 0);

		fprintf(stderr, _("\nLogin incorrect\n"));
		pam_end(pamh, rc);
		sleepexit(EXIT_SUCCESS);
	}
}
Exemple #5
0
/*
 * Authentication thread.
 */
static void *
sshpam_thread(void *ctxtp)
{
	struct pam_ctxt *ctxt = ctxtp;
	Buffer buffer;
	struct pam_conv sshpam_conv;
	int flags = (options.permit_empty_passwd == 0 ?
	    PAM_DISALLOW_NULL_AUTHTOK : 0);
#ifndef UNSUPPORTED_POSIX_THREADS_HACK
	extern char **environ;
	char **env_from_pam;
	u_int i;
	const char *pam_user;
	const char **ptr_pam_user = &pam_user;
	char *tz = getenv("TZ");

	sshpam_err = pam_get_item(sshpam_handle, PAM_USER,
	    (sshpam_const void **)ptr_pam_user);
	if (sshpam_err != PAM_SUCCESS)
		goto auth_fail;

	environ[0] = NULL;
	if (tz != NULL)
		if (setenv("TZ", tz, 1) == -1)
			error("PAM: could not set TZ environment: %s",
			    strerror(errno));

	if (sshpam_authctxt != NULL) {
		setproctitle("%s [pam]",
		    sshpam_authctxt->valid ? pam_user : "******");
	}
#endif

	sshpam_conv.conv = sshpam_thread_conv;
	sshpam_conv.appdata_ptr = ctxt;

	if (sshpam_authctxt == NULL)
		fatal("%s: PAM authctxt not initialized", __func__);

	buffer_init(&buffer);
	sshpam_err = pam_set_item(sshpam_handle, PAM_CONV,
	    (const void *)&sshpam_conv);
	if (sshpam_err != PAM_SUCCESS)
		goto auth_fail;
	sshpam_err = pam_authenticate(sshpam_handle, flags);
	if (sshpam_err == PAM_MAXTRIES)
		sshpam_set_maxtries_reached(1);
	if (sshpam_err != PAM_SUCCESS)
		goto auth_fail;

	if (compat20) {
		if (!do_pam_account()) {
			sshpam_err = PAM_ACCT_EXPIRED;
			goto auth_fail;
		}
		if (sshpam_authctxt->force_pwchange) {
			sshpam_err = pam_chauthtok(sshpam_handle,
			    PAM_CHANGE_EXPIRED_AUTHTOK);
			if (sshpam_err != PAM_SUCCESS)
				goto auth_fail;
			sshpam_password_change_required(0);
		}
	}

	buffer_put_cstring(&buffer, "OK");

#ifndef UNSUPPORTED_POSIX_THREADS_HACK
	/* Export variables set by do_pam_account */
	buffer_put_int(&buffer, sshpam_account_status);
	buffer_put_int(&buffer, sshpam_authctxt->force_pwchange);

	/* Export any environment strings set in child */
	for(i = 0; environ[i] != NULL; i++)
		; /* Count */
	buffer_put_int(&buffer, i);
	for(i = 0; environ[i] != NULL; i++)
		buffer_put_cstring(&buffer, environ[i]);

	/* Export any environment strings set by PAM in child */
	env_from_pam = pam_getenvlist(sshpam_handle);
	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
		; /* Count */
	buffer_put_int(&buffer, i);
	for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++)
		buffer_put_cstring(&buffer, env_from_pam[i]);
#endif /* UNSUPPORTED_POSIX_THREADS_HACK */

	/* XXX - can't do much about an error here */
	ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer);
	buffer_free(&buffer);
	pthread_exit(NULL);

 auth_fail:
	buffer_put_cstring(&buffer,
	    pam_strerror(sshpam_handle, sshpam_err));
	/* XXX - can't do much about an error here */
	if (sshpam_err == PAM_ACCT_EXPIRED)
		ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer);
	else if (sshpam_maxtries_reached)
		ssh_msg_send(ctxt->pam_csock, PAM_MAXTRIES, &buffer);
	else
		ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer);
	buffer_free(&buffer);
	pthread_exit(NULL);

	return (NULL); /* Avoid warning for non-pthread case */
}
PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
                 int argc, const char **argv)
{
        int ctrl;
        struct module_options options;

        memset(&options, 0, sizeof(options));
        options.retry_times = CO_RETRY_TIMES;

        ctrl = _pam_parse(pamh, &options, argc, argv);
        if (ctrl < 0)
                return PAM_BUF_ERR;

        if (flags & PAM_PRELIM_CHECK) {
                /* Check for passwd dictionary
                 * We cannot do that, since the original path is compiled
                 * into the cracklib library and we don't know it.
                 */
                return PAM_SUCCESS;
        } else if (flags & PAM_UPDATE_AUTHTOK) {
                int retval;
                const void *oldtoken;
                const char *user;
                int tries;

                retval = pam_get_user(pamh, &user, NULL);
                if (retval != PAM_SUCCESS || user == NULL) {
                        if (ctrl & PAM_DEBUG_ARG)
                                pam_syslog(pamh, LOG_ERR, "Can not get username");
                        return PAM_AUTHTOK_ERR;
                }

                retval = pam_get_item(pamh, PAM_OLDAUTHTOK, &oldtoken);
                if (retval != PAM_SUCCESS) {
                        if (ctrl & PAM_DEBUG_ARG)
                                pam_syslog(pamh, LOG_ERR, "Can not get old passwd");
                        oldtoken = NULL;
                }

                tries = 0;
                while (tries < options.retry_times) {
                        void *auxerror;
                        const char *newtoken = NULL;

                        tries++;

                        /* Planned modus operandi:
                         * Get a passwd.
                         * Verify it against libpwquality.
                         * If okay get it a second time.
                         * Check to be the same with the first one.
                         * set PAM_AUTHTOK and return
                         */

                        retval = pam_get_authtok_noverify(pamh, &newtoken, NULL);
                        if (retval != PAM_SUCCESS) {
                                pam_syslog(pamh, LOG_ERR, "pam_get_authtok_noverify returned error: %s",
                                        pam_strerror(pamh, retval));
                                continue;
                        } else if (newtoken == NULL) { /* user aborted password change, quit */
                                return PAM_AUTHTOK_ERR;
                        }

                        /* now test this passwd against libpwquality */
                        retval = pwquality_check(options.pwq, newtoken, oldtoken, user, &auxerror);

                        if (retval < 0) {
                                const char *msg;
                                char buf[PWQ_MAX_ERROR_MESSAGE_LEN];
                                msg = pwquality_strerror(buf, sizeof(buf), retval, auxerror);
                                if (ctrl & PAM_DEBUG_ARG)
                                        pam_syslog(pamh, LOG_DEBUG, "bad password: %s", msg);
                                pam_error(pamh, _("BAD PASSWORD: %s"), msg);

                                if (getuid() || options.enforce_for_root ||
                                    (flags & PAM_CHANGE_EXPIRED_AUTHTOK)) {
                                        pam_set_item(pamh, PAM_AUTHTOK, NULL);
                                        retval = PAM_AUTHTOK_ERR;
                                        continue;
                                }
                        } else {
                                if (ctrl & PAM_DEBUG_ARG)
                                        pam_syslog(pamh, LOG_DEBUG, "password score: %d", retval);
                        }

                        retval = pam_get_authtok_verify(pamh, &newtoken, NULL);
                        if (retval != PAM_SUCCESS) {
                                pam_syslog(pamh, LOG_ERR, "pam_get_authtok_verify returned error: %s",
                                pam_strerror(pamh, retval));
                                pam_set_item(pamh, PAM_AUTHTOK, NULL);
                                continue;
                        } else if (newtoken == NULL) {      /* user aborted password change, quit */
                                return PAM_AUTHTOK_ERR;
                        }

                        return PAM_SUCCESS;
                }

                pam_set_item (pamh, PAM_AUTHTOK, NULL);

                /* if we have only one try, we can use the real reason,
                 * else say that there were too many tries. */
                if (options.retry_times > 1)
                        return PAM_MAXTRIES;
                else
                        return retval;
        } else {
                if (ctrl & PAM_DEBUG_ARG)
                        pam_syslog(pamh, LOG_NOTICE, "UNKNOWN flags setting %02X",flags);
        }

        return PAM_SERVICE_ERR;
}
/*
 * Check authentication against PAM.
 */
static int
CheckPAMAuth(Port *port, char *user, char *password)
{
	int			retval;
	pam_handle_t *pamh = NULL;

	/*
	 * Apparently, Solaris 2.6 is broken, and needs ugly static variable
	 * workaround
	 */
	pam_passwd = password;

	/*
	 * Set the application data portion of the conversation struct This is
	 * later used inside the PAM conversation to pass the password to the
	 * authentication module.
	 */
	pam_passw_conv.appdata_ptr = (char *) password;		/* from password above,
														 * not allocated */

	/* Optionally, one can set the service name in pg_hba.conf */
	if (port->auth_arg && port->auth_arg[0] != '\0')
		retval = pam_start(port->auth_arg, "pgsql@",
						   &pam_passw_conv, &pamh);
	else
		retval = pam_start(PGSQL_PAM_SERVICE, "pgsql@",
						   &pam_passw_conv, &pamh);

	if (retval != PAM_SUCCESS)
	{
		ereport(LOG,
				(errmsg("could not create PAM authenticator: %s",
						pam_strerror(pamh, retval))));
		pam_passwd = NULL;		/* Unset pam_passwd */
		return STATUS_ERROR;
	}

	retval = pam_set_item(pamh, PAM_USER, user);

	if (retval != PAM_SUCCESS)
	{
		ereport(LOG,
				(errmsg("pam_set_item(PAM_USER) failed: %s",
						pam_strerror(pamh, retval))));
		pam_passwd = NULL;		/* Unset pam_passwd */
		return STATUS_ERROR;
	}

	retval = pam_set_item(pamh, PAM_CONV, &pam_passw_conv);

	if (retval != PAM_SUCCESS)
	{
		ereport(LOG,
				(errmsg("pam_set_item(PAM_CONV) failed: %s",
						pam_strerror(pamh, retval))));
		pam_passwd = NULL;		/* Unset pam_passwd */
		return STATUS_ERROR;
	}

	retval = pam_authenticate(pamh, 0);

	if (retval != PAM_SUCCESS)
	{
		ereport(LOG,
				(errmsg("pam_authenticate failed: %s",
						pam_strerror(pamh, retval))));
		pam_passwd = NULL;		/* Unset pam_passwd */
		return STATUS_ERROR;
	}

	retval = pam_acct_mgmt(pamh, 0);

	if (retval != PAM_SUCCESS)
	{
		ereport(LOG,
				(errmsg("pam_acct_mgmt failed: %s",
						pam_strerror(pamh, retval))));
		pam_passwd = NULL;		/* Unset pam_passwd */
		return STATUS_ERROR;
	}

	retval = pam_end(pamh, retval);

	if (retval != PAM_SUCCESS)
	{
		ereport(LOG,
				(errmsg("could not release PAM authenticator: %s",
						pam_strerror(pamh, retval))));
	}

	pam_passwd = NULL;			/* Unset pam_passwd */

	return (retval == PAM_SUCCESS ? STATUS_OK : STATUS_ERROR);
}
int userAuth(const char *uname,char *password)
{
	int ofd;
	pam_handle_t *pamh;
	int pam_status;
	int ret;
	char t_uname[256];

	struct passwd *p1;
	strcpy(t_uname,uname);

	printf("******userAuth.c******************************\n");
	printf("UserName:%s and password:%s\n",uname,password);
	printf("**********************************************\n");

	/* quick fix password is stored in flat file and deleted after performing the action */
	
	if ( (ofd=open("/tmp/passfile",O_WRONLY|O_CREAT,S_IRWXU)) < 0 ) {
		perror("open ");
		exit(0);
	}
	write(ofd,password,strlen(password));
	close(ofd);
	dup2(0,10);
	close(0);
	if ( (ofd=open("/tmp/passfile",O_RDONLY)) < 0 ) {
		perror("reading password file ");
		printf("uid of this process...:%d\n",getuid());
		exit(0);
	}
	
	/* PAM AUTHENTICATION MODULE */
	pam_status = pam_start("lsb_login",NULL, &conv, &pamh);
	pam_status = pam_set_item(pamh,PAM_USER,uname);

	if (pam_status == PAM_SUCCESS)
		pam_status = pam_authenticate(pamh, PAM_SILENT);
	printf("pam_authenticate call done...\n");
	if (pam_status == PAM_SUCCESS) {
		pam_get_item(pamh, PAM_USER, (const void **)&uname);
		fprintf(stdout, "Greetings %s\n", uname);
		ret = 1;
	} else {
		printf("%s\n", pam_strerror(pamh, pam_status));
		ret = 0;
	}
	pam_end(pamh, pam_status);
	
	/*Finally remove the temp pass file */	
	unlink("/tmp/passfile");
	close(ofd);
	dup2(10,0);
	if ( ret == 0)	//authentication failed
		return -1;
	else {		//authentication success
		/* get the userid for this user and return this val */
		printf("Getting userid...\n");
		if ( (p1=getpwnam(t_uname)) != NULL ) {
			printf("Userid for %s is :%d\n",t_uname,p1->pw_uid);
			return(p1->pw_uid);
		}
	}
	return -1;
}
Exemple #9
0
/*
 * main - userdel command
 */
int main (int argc, char **argv)
{
	int errors = 0; /* Error in the removal of the home directory */

#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
	pam_handle_t *pamh = NULL;
	int retval;
#endif				/* USE_PAM */
#endif				/* ACCT_TOOLS_SETUID */

	/*
	 * Get my name so that I can use it to report errors.
	 */
	Prog = Basename (argv[0]);
	(void) setlocale (LC_ALL, "");
	(void) bindtextdomain (PACKAGE, LOCALEDIR);
	(void) textdomain (PACKAGE);

	process_root_flag ("-R", argc, argv);

	OPENLOG ("userdel");
#ifdef WITH_AUDIT
	audit_help_open ();
#endif				/* WITH_AUDIT */

	{
		/*
		 * Parse the command line options.
		 */
		int c;
		static struct option long_options[] = {
			{"force",        no_argument,       NULL, 'f'},
			{"help",         no_argument,       NULL, 'h'},
			{"remove",       no_argument,       NULL, 'r'},
			{"root",         required_argument, NULL, 'R'},
#ifdef WITH_SELINUX
			{"selinux-user", no_argument,       NULL, 'Z'},
#endif				/* WITH_SELINUX */
			{NULL, 0, NULL, '\0'}
		};
		while ((c = getopt_long (argc, argv,
#ifdef WITH_SELINUX             
		                         "fhrR:Z",
#else				/* !WITH_SELINUX */
		                         "fhrR:",
#endif				/* !WITH_SELINUX */
		                         long_options, NULL)) != -1) {
			switch (c) {
			case 'f':	/* force remove even if not owned by user */
				fflg = true;
				break;
			case 'h':
				usage (E_SUCCESS);
				break;
			case 'r':	/* remove home dir and mailbox */
				rflg = true;
				break;
			case 'R': /* no-op, handled in process_root_flag () */
				break;
#ifdef WITH_SELINUX             
			case 'Z':
				if (is_selinux_enabled () > 0) {
					Zflg = true;
				} else {
					fprintf (stderr,
					         _("%s: -Z requires SELinux enabled kernel\n"),
					         Prog);

					exit (E_BAD_ARG);
				}
				break;
#endif				/* WITH_SELINUX */
			default:
				usage (E_USAGE);
			}
		}
	}

	if ((optind + 1) != argc) {
		usage (E_USAGE);
	}

#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
	{
		struct passwd *pampw;
		pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
		if (pampw == NULL) {
			fprintf (stderr,
			         _("%s: Cannot determine your user name.\n"),
			         Prog);
			exit (E_PW_UPDATE);
		}

		retval = pam_start ("userdel", pampw->pw_name, &conv, &pamh);
	}

	if (PAM_SUCCESS == retval) {
		retval = pam_authenticate (pamh, 0);
	}

	if (PAM_SUCCESS == retval) {
		retval = pam_acct_mgmt (pamh, 0);
	}

	if (PAM_SUCCESS != retval) {
		fprintf (stderr, _("%s: PAM: %s\n"),
		         Prog, pam_strerror (pamh, retval));
		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
		if (NULL != pamh) {
			(void) pam_end (pamh, retval);
		}
		exit (E_PW_UPDATE);
	}
	(void) pam_end (pamh, retval);
#endif				/* USE_PAM */
#endif				/* ACCT_TOOLS_SETUID */

	is_shadow_pwd = spw_file_present ();
#ifdef SHADOWGRP
	is_shadow_grp = sgr_file_present ();
#endif				/* SHADOWGRP */
#ifdef ENABLE_SUBIDS
	is_sub_uid = sub_uid_file_present ();
	is_sub_gid = sub_gid_file_present ();
#endif				/* ENABLE_SUBIDS */

	/*
	 * Start with a quick check to see if the user exists.
	 */
	user_name = argv[argc - 1];
	{
		struct passwd *pwd;
		pwd = getpwnam (user_name); /* local, no need for xgetpwnam */
		if (NULL == pwd) {
			fprintf (stderr, _("%s: user '%s' does not exist\n"),
				 Prog, user_name);
#ifdef WITH_AUDIT
			audit_logger (AUDIT_DEL_USER, Prog,
			              "deleting user not found",
			              user_name, AUDIT_NO_ID,
			              SHADOW_AUDIT_FAILURE);
#endif				/* WITH_AUDIT */
			exit (E_NOTFOUND);
		}
		user_id = pwd->pw_uid;
		user_gid = pwd->pw_gid;
		user_home = xstrdup (pwd->pw_dir);
	}
#ifdef WITH_TCB
	if (shadowtcb_set_user (user_name) == SHADOWTCB_FAILURE) {
		exit (E_NOTFOUND);
	}
#endif				/* WITH_TCB */
#ifdef	USE_NIS

	/*
	 * Now make sure it isn't an NIS user.
	 */
	if (__ispwNIS ()) {
		char *nis_domain;
		char *nis_master;

		fprintf (stderr,
		         _("%s: user %s is a NIS user\n"), Prog, user_name);
		if (   !yp_get_default_domain (&nis_domain)
		    && !yp_master (nis_domain, "passwd.byname", &nis_master)) {
			fprintf (stderr,
			         _("%s: %s is the NIS master\n"),
			         Prog, nis_master);
		}
		exit (E_NOTFOUND);
	}
#endif				/* USE_NIS */
	/*
	 * Check to make certain the user isn't logged in.
	 * Note: This is a best effort basis. The user may log in between,
	 * a cron job may be started on her behalf, etc.
	 */
	if (user_busy (user_name, user_id) != 0) {
		if (!fflg) {
#ifdef WITH_AUDIT
			audit_logger (AUDIT_DEL_USER, Prog,
			              "deleting user logged in",
			              user_name, AUDIT_NO_ID,
			              SHADOW_AUDIT_FAILURE);
#endif				/* WITH_AUDIT */
			exit (E_USER_BUSY);
		}
	}

	/*
	 * Do the hard stuff - open the files, create the user entries,
	 * create the home directory, then close and update the files.
	 */
	open_files ();
	update_user ();
	update_groups ();

	if (rflg) {
		errors += remove_mailbox ();
	}
	if (rflg) {
		int home_owned = is_owner (user_id, user_home);
		if (-1 == home_owned) {
			fprintf (stderr,
			         _("%s: %s home directory (%s) not found\n"),
			         Prog, user_name, user_home);
			rflg = 0;
		} else if ((0 == home_owned) && !fflg) {
			fprintf (stderr,
			         _("%s: %s not owned by %s, not removing\n"),
			         Prog, user_home, user_name);
			rflg = 0;
			errors++;
			/* continue */
		}
	}

#ifdef EXTRA_CHECK_HOME_DIR
	/* This may be slow, the above should be good enough. */
	if (rflg && !fflg) {
		struct passwd *pwd;
		/*
		 * For safety, refuse to remove the home directory if it
		 * would result in removing some other user's home
		 * directory. Still not perfect so be careful, but should
		 * prevent accidents if someone has /home or / as home
		 * directory...  --marekm
		 */
		setpwent ();
		while ((pwd = getpwent ())) {
			if (strcmp (pwd->pw_name, user_name) == 0) {
				continue;
			}
			if (path_prefix (user_home, pwd->pw_dir)) {
				fprintf (stderr,
				         _("%s: not removing directory %s (would remove home of user %s)\n"),
				         Prog, user_home, pwd->pw_name);
				rflg = false;
				errors++;
				/* continue */
				break;
			}
		}
		endpwent ();
	}
#endif				/* EXTRA_CHECK_HOME_DIR */

	if (rflg) {
		if (remove_tree (user_home, true) != 0) {
			fprintf (stderr,
			         _("%s: error removing directory %s\n"),
			         Prog, user_home);
			errors++;
			/* continue */
		}
#ifdef WITH_AUDIT
		else
		{
			audit_logger (AUDIT_DEL_USER, Prog,
			              "deleting home directory",
			              user_name, (unsigned int) user_id,
			              SHADOW_AUDIT_SUCCESS);
		}
#endif				/* WITH_AUDIT */
	}
#ifdef WITH_AUDIT
	if (0 != errors) {
		audit_logger (AUDIT_DEL_USER, Prog,
		              "deleting home directory",
		              user_name, AUDIT_NO_ID,
		              SHADOW_AUDIT_FAILURE);
	}
#endif				/* WITH_AUDIT */

#ifdef WITH_SELINUX
	if (Zflg) {
		if (del_seuser (user_name) != 0) {
			fprintf (stderr,
			         _("%s: warning: the user name %s to SELinux user mapping removal failed.\n"),
			         Prog, user_name);
#ifdef WITH_AUDIT
			audit_logger (AUDIT_ADD_USER, Prog,
			              "removing SELinux user mapping",
			              user_name, (unsigned int) user_id,
			              SHADOW_AUDIT_FAILURE);
#endif				/* WITH_AUDIT */
			fail_exit (E_SE_UPDATE);
		}
	}
#endif				/* WITH_SELINUX */

	/*
	 * Cancel any crontabs or at jobs. Have to do this before we remove
	 * the entry from /etc/passwd.
	 */
	user_cancel (user_name);
	close_files ();

#ifdef WITH_TCB
	errors += remove_tcbdir (user_name, user_id);
#endif				/* WITH_TCB */

	nscd_flush_cache ("passwd");
	nscd_flush_cache ("group");

	return ((0 != errors) ? E_HOMEDIR : E_SUCCESS);
}
Exemple #10
0
static void
run_file(const char *filename, uid_t uid, gid_t gid)
{
/* Run a file by spawning off a process which redirects I/O,
 * spawns a subshell, then waits for it to complete and sends
 * mail to the user.
 */
    pid_t pid;
    int fd_out, fd_in;
    int queue;
    char mailbuf[LOGNAMESIZE + 1], fmt[49];
    char *mailname = NULL;
    FILE *stream;
    int send_mail = 0;
    struct stat buf, lbuf;
    off_t size;
    struct passwd *pentry;
    int fflags;
    uid_t nuid;
    gid_t ngid;
#ifdef PAM
    pam_handle_t *pamh = NULL;
    int pam_err;
    struct pam_conv pamc = {
	.conv = openpam_nullconv,
	.appdata_ptr = NULL
    };
#endif

    PRIV_START

    if (chmod(filename, S_IRUSR) != 0)
    {
	perr("cannot change file permissions");
    }

    PRIV_END

    pid = fork();
    if (pid == -1)
	perr("cannot fork");
    
    else if (pid != 0)
	return;

    /* Let's see who we mail to.  Hopefully, we can read it from
     * the command file; if not, send it to the owner, or, failing that,
     * to root.
     */

    pentry = getpwuid(uid);
    if (pentry == NULL)
	perrx("Userid %lu not found - aborting job %s",
		(unsigned long) uid, filename);

#ifdef PAM
    PRIV_START

    pam_err = pam_start(atrun, pentry->pw_name, &pamc, &pamh);
    if (pam_err != PAM_SUCCESS)
	perrx("cannot start PAM: %s", pam_strerror(pamh, pam_err));

    pam_err = pam_acct_mgmt(pamh, PAM_SILENT);
    /* Expired password shouldn't prevent the job from running. */
    if (pam_err != PAM_SUCCESS && pam_err != PAM_NEW_AUTHTOK_REQD)
	perrx("Account %s (userid %lu) unavailable for job %s: %s",
	    pentry->pw_name, (unsigned long)uid,
	    filename, pam_strerror(pamh, pam_err));

    pam_end(pamh, pam_err);

    PRIV_END
#endif /* PAM */

    PRIV_START

    stream=fopen(filename, "r");

    PRIV_END

    if (stream == NULL)
	perr("cannot open input file");

    if ((fd_in = dup(fileno(stream))) <0)
	perr("error duplicating input file descriptor");

    if (fstat(fd_in, &buf) == -1)
	perr("error in fstat of input file descriptor");

    if (lstat(filename, &lbuf) == -1)
	perr("error in fstat of input file");

    if (S_ISLNK(lbuf.st_mode))
	perrx("Symbolic link encountered in job %s - aborting", filename);

    if ((lbuf.st_dev != buf.st_dev) || (lbuf.st_ino != buf.st_ino) ||
        (lbuf.st_uid != buf.st_uid) || (lbuf.st_gid != buf.st_gid) ||
        (lbuf.st_size!=buf.st_size))
	perrx("Somebody changed files from under us for job %s - aborting",
		filename);

    if (buf.st_nlink > 1)
	perrx("Somebody is trying to run a linked script for job %s", filename);

    if ((fflags = fcntl(fd_in, F_GETFD)) <0)
	perr("error in fcntl");

    fcntl(fd_in, F_SETFD, fflags & ~FD_CLOEXEC);

    snprintf(fmt, sizeof(fmt),
	"#!/bin/sh\n# atrun uid=%%ld gid=%%ld\n# mail %%%ds %%d",
                          LOGNAMESIZE);

    if (fscanf(stream, fmt, &nuid, &ngid, mailbuf, &send_mail) != 4)
	perrx("File %s is in wrong format - aborting", filename);

    if (mailbuf[0] == '-')
	perrx("Illegal mail name %s in %s", mailbuf, filename);

    mailname = mailbuf;

    if (nuid != uid)
	perrx("Job %s - userid %u does not match file uid %u",
		filename, nuid, uid);

    if (ngid != gid)
	perrx("Job %s - groupid %u does not match file gid %u",
		filename, ngid, gid);

    fclose(stream);

    if (chdir(ATSPOOL_DIR) < 0)
	perr("cannot chdir to %s", ATSPOOL_DIR);
    
    /* Create a file to hold the output of the job we are about to run.
     * Write the mail header.
     */    
    if((fd_out=open(filename,
		O_WRONLY | O_CREAT | O_EXCL, S_IWUSR | S_IRUSR)) < 0)
	perr("cannot create output file");

    write_string(fd_out, "Subject: Output from your job ");
    write_string(fd_out, filename);
    write_string(fd_out, "\n\n");
    fstat(fd_out, &buf);
    size = buf.st_size;

    close(STDIN_FILENO);
    close(STDOUT_FILENO);
    close(STDERR_FILENO);
 
    pid = fork();
    if (pid < 0)
	perr("error in fork");

    else if (pid == 0)
    {
	char *nul = NULL;
	char **nenvp = &nul;

	/* Set up things for the child; we want standard input from the input file,
	 * and standard output and error sent to our output file.
	 */

	if (lseek(fd_in, (off_t) 0, SEEK_SET) < 0)
	    perr("error in lseek");

	if (dup(fd_in) != STDIN_FILENO)
	    perr("error in I/O redirection");

	if (dup(fd_out) != STDOUT_FILENO)
	    perr("error in I/O redirection");

	if (dup(fd_out) != STDERR_FILENO)
	    perr("error in I/O redirection");

	close(fd_in);
	close(fd_out);
	if (chdir(ATJOB_DIR) < 0)
	    perr("cannot chdir to %s", ATJOB_DIR);

	queue = *filename;

	PRIV_START

        nice(tolower(queue) - 'a');
	
#ifdef LOGIN_CAP
	/*
	 * For simplicity and safety, set all aspects of the user context
	 * except for a selected subset:  Don't set priority, which was
	 * set based on the queue file name according to the tradition.
	 * Don't bother to set environment, including path vars, either
	 * because it will be discarded anyway.  Although the job file
	 * should set umask, preset it here just in case.
	 */
	if (setusercontext(NULL, pentry, uid, LOGIN_SETALL &
		~(LOGIN_SETPRIORITY | LOGIN_SETPATH | LOGIN_SETENV)) != 0)
	    exit(EXIT_FAILURE);	/* setusercontext() logged the error */
#else /* LOGIN_CAP */
	if (initgroups(pentry->pw_name,pentry->pw_gid))
	    perr("cannot init group access list");

	if (setgid(gid) < 0 || setegid(pentry->pw_gid) < 0)
	    perr("cannot change group");

	if (setlogin(pentry->pw_name))
	    perr("cannot set login name");

	if (setuid(uid) < 0 || seteuid(uid) < 0)
	    perr("cannot set user id");
#endif /* LOGIN_CAP */

	if (chdir(pentry->pw_dir))
		chdir("/");

	if(execle("/bin/sh","sh",NULL, nenvp) != 0)
	    perr("exec failed for /bin/sh");

	PRIV_END
    }
Exemple #11
0
PAM_EXTERN int pam_sm_authenticate(pam_handle_t * pamh,
                                   int flags, int argc, const char **argv)
{

    const char *user = NULL, *passwd = NULL;
    struct passwd *pwd;
    int rval, status;
    pid_t pid;


    // For checking mount paths: (mount from + target)
    char path[PATH_MAX];
    char targetpath[PATH_MAX];
    char encfs_options[USERNAME_MAX];
    char fuse_options[USERNAME_MAX];
    char *targetpath_store;

    strcpy(default_encfs_options, "");
    strcpy(default_fuse_options, "");

    // For execing:
    char *arg[USERNAME_MAX];
    int arg_pos = 0;
    int i;
    int inpipe[2], outpipe[2];

    rval = pam_get_user(pamh, &user, NULL);
    if ((rval != PAM_SUCCESS) || (!user))
    {
        _pam_log(LOG_ERR, "can't get username: %s", pam_strerror(pamh, rval));
        return PAM_AUTH_ERR;
    }

    rval = pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd);
    if (rval != PAM_SUCCESS)
    {
        _pam_log(LOG_ERR, "Could not retrieve user's password");
        return PAM_AUTH_ERR;
    }

    if (!passwd)
    {
        rval = _set_auth_tok(pamh, flags, argc, argv);
        if (rval != PAM_SUCCESS)
        {
            return rval;
        }
        rval =
            pam_get_item(pamh, PAM_AUTHTOK, (const void **) (void *) &passwd);
        if (rval != PAM_SUCCESS || passwd == NULL)
        {
            _pam_log(LOG_ERR, "Could not retrieve user's password");
            return PAM_AUTH_ERR;
        }
    }
    if ((pwd = getpwnam(user)) == NULL)
    {
        _pam_log(LOG_ERR, "Could not getpwnam");
        return PAM_AUTH_ERR;
    }

    // Read configfile  
    if (!readconfig
        (pwd, pamh, pwd->pw_name, path, targetpath, encfs_options,
         fuse_options))
    {
        // DEBUG _pam_log(LOG_ERR,"No entry for user found in log");
        return PAM_IGNORE;
    }
    //DEBUG _pam_log(LOG_ERR,"Username : %s, Encpath : %s, Targetmount : %s",pwd->pw_name,path,targetpath);

    //Store targetpath
    targetpath_store = strdup(targetpath);
    if ((i =
         pam_set_data(pamh, "encfs_targetpath", targetpath_store,
                      targetpath_cleanup)) != PAM_SUCCESS)
    {
        _pam_log(LOG_ERR, "Storing targetpath FAIL");
        free(targetpath_store);
        return i;
    }

    // Check if we're mounted already.
    if (checkmnt(targetpath))
    {
        //DEBUG _pam_log(LOG_ERR,"Already mounted");
        return PAM_IGNORE;
    }



    /*  _pam_log(LOG_ERR,"Config output for %s:",user);
       _pam_log(LOG_ERR,"  path       : %s",path);
       _pam_log(LOG_ERR,"  targetpath : %s",targetpath);
       _pam_log(LOG_ERR,"  encfs      : %s %s",default_encfs_options,encfs_options);
       _pam_log(LOG_ERR,"  fuse       : %s %s",default_fuse_options,fuse_options); */


    arg_pos += buildCmd(arg, arg_pos, "encfs");
    arg_pos += buildCmd(arg, arg_pos, "-S");
    arg_pos += buildCmd(arg, arg_pos, default_encfs_options);
    arg_pos += buildCmd(arg, arg_pos, encfs_options);
    arg_pos += buildCmd(arg, arg_pos, path);
    arg_pos += buildCmd(arg, arg_pos, targetpath);

    if (strlen(default_fuse_options) > 0 && strlen(fuse_options) > 0)
        strcat(fuse_options, ",");

    strcat(fuse_options,default_fuse_options);
    if (strlen(fuse_options) > 0) {
        arg_pos += buildCmd(arg, arg_pos, "--");
        arg_pos += buildCmd(arg, arg_pos, "-o");
        arg_pos += buildCmd(arg, arg_pos, fuse_options);
    }
    arg[arg_pos] = NULL;

    /*  printf("Arguments : ");
       for (i = 0; i < arg_pos+1;i++) {
       _pam_log(LOG_ERR,"Data : %s",arg[i]);
       }

       _pam_log(LOG_ERR,"Number of arguments : %d",arg_pos); */


    /*  arg[0] = cmd;
       arg[1] = params;
       //  arg[2] = params2;
       arg[2] = params3;
       arg[3] = path;
       arg[4] = targetpath;
       arg[5] = fuseparams;
       arg[6] = fuseparams2;
       arg[7] = NULL; */



    if (pipe(inpipe) || pipe(outpipe))
    {
        _pam_log(LOG_ERR, "Failed to create pipe");
        return PAM_IGNORE;
    }

    // Execute 
    switch (pid = fork())
    {
        case -1:
            _pam_log(LOG_ERR, "Fork failed");
            return PAM_SERVICE_ERR;
        case 0:

            if (drop_permissions == 1)
                if ((initgroups(pwd->pw_name, pwd->pw_gid) == -1)
                    || (setgid(pwd->pw_gid) == -1)
                    || (setuid(pwd->pw_uid) == -1))
                {
                    _pam_log(LOG_ERR, "Dropping permissions failed");
                    return PAM_SERVICE_ERR;
                }
            close(outpipe[WRITE_END]);
            dup2(outpipe[READ_END], fileno(stdin));
            close(outpipe[READ_END]);

            close(inpipe[READ_END]);
            dup2(inpipe[WRITE_END], fileno(stdout));
            close(inpipe[WRITE_END]);

            // For some reason the current directory has to be set to targetpath (or path?) before exec'ing encfs through gdm
            chdir(targetpath);
            execvp("encfs", arg);
            char errstr[128];

            snprintf(errstr, 127, "%d - %s", errno, strerror(errno));
            _pam_log(LOG_ERR, "Exec failed - %s", errstr);
            exit(127);
    }

    int len;


    close(inpipe[WRITE_END]);
    close(outpipe[READ_END]);




    if (waitpid(pid, &status, WNOHANG) == 0)
    {
        len = write(outpipe[WRITE_END], passwd, (size_t) strlen(passwd));
        if ((len != (size_t) strlen(passwd))
            || (write(outpipe[WRITE_END], "\n", 1) != 1))
            _pam_log(LOG_ERR, "Did not send password to pipe (%d sent)", len);
        close(outpipe[WRITE_END]);
    }


    if (waitpid_timeout(pid, &status, 0))
    {
        _pam_log(LOG_ERR, "Timed out waiting for encfs, killing\n");
        kill(pid, SIGKILL);
    }

    int exitstatus = WEXITSTATUS(status);
    char buff[512];

    len = read(inpipe[READ_END], &buff, 511);
    close(inpipe[READ_END]);
    buff[len] = 0;
    if (!checkmnt(targetpath) && (len > 0 || exitstatus > 0))
    {
        _pam_log(LOG_ERR, "exitcode : %d, errorstring : %s", exitstatus,
                 buff);
        return PAM_AUTH_ERR;
    }
    else
    {
        return PAM_IGNORE;
    }
    return PAM_AUTH_ERR;
}
Exemple #12
0
static int _pam_dispatch_aux(pam_handle_t *pamh, int flags, struct handler *h,
			     _pam_boolean resumed, int use_cached_chain)
{
    int depth, impression, status, skip_depth;

    IF_NO_PAMH("_pam_dispatch_aux", pamh, PAM_SYSTEM_ERR);

    if (h == NULL) {
	const char *service=NULL;

	(void) pam_get_item(pamh, PAM_SERVICE, (const void **)&service);
	_pam_system_log(LOG_ERR, "no modules loaded for `%s' service",
			service ? service:"<unknown>" );
	service = NULL;
	return PAM_MUST_FAIL_CODE;
    }

    /* if we are recalling this module stack because a former call did
       not complete, we restore the state of play from pamh. */
    if (resumed) {
	skip_depth = pamh->former.depth;
	status = pamh->former.status;
	impression = pamh->former.impression;
	/* forget all that */
	pamh->former.impression = _PAM_UNDEF;
	pamh->former.status = PAM_MUST_FAIL_CODE;
	pamh->former.depth = 0;
    } else {
	skip_depth = 0;
	impression = _PAM_UNDEF;
	status = PAM_MUST_FAIL_CODE;
    }

    /* Loop through module logic stack */
    for (depth=0 ; h != NULL ; h = h->next, ++depth) {
	int retval, cached_retval, action;

	/* skip leading modules if they have already returned */
	if (depth < skip_depth) {
	    continue;
	}

	/* attempt to call the module */
	if (h->func == NULL) {
	    D(("module function is not defined, indicating failure"));
	    retval = PAM_MODULE_UNKNOWN;
	} else {
	    D(("passing control to module..."));
	    retval = h->func(pamh, flags, h->argc, h->argv);
	    D(("module returned: %s", pam_strerror(pamh, retval)));
	    if (h->must_fail) {
		D(("module poorly listed in PAM config; forcing failure"));
		retval = PAM_MUST_FAIL_CODE;
	    }
	}

	/*
	 * PAM_INCOMPLETE return is special.  It indicates that the
	 * module wants to wait for the application before continuing.
	 * In order to return this, the module will have saved its
	 * state so it can resume from an equivalent position when it
	 * is called next time.  (This was added as of 0.65)
	 */
	if (retval == PAM_INCOMPLETE) {
	    pamh->former.impression = impression;
	    pamh->former.status = status;
	    pamh->former.depth = depth;

	    D(("module %d returned PAM_INCOMPLETE", depth));
	    return retval;
	}

	if (use_cached_chain) {
	    /* a former stack execution has frozen the chain */
	    cached_retval = *(h->cached_retval_p);
	} else {
	    /* this stack execution is defining the frozen chain */
	    cached_retval = h->cached_retval = retval;
	}

	/* verify that the return value is a valid one */
	if ((cached_retval < PAM_SUCCESS)
	    || (cached_retval >= _PAM_RETURN_VALUES)) {
	    retval = PAM_MUST_FAIL_CODE;
	    action = _PAM_ACTION_BAD;
	} else {
	    /* We treat the current retval with some respect. It may
	       (for example, in the case of setcred) have a value that
	       needs to be propagated to the user.  We want to use the
	       cached_retval to determine the modules to be executed
	       in the stacked chain, but we want to treat each
	       non-ignored module in the cached chain as now being
	       'required'. We only need to treat the,
	       _PAM_ACTION_IGNORE, _PAM_ACTION_IS_JUMP and
	       _PAM_ACTION_RESET actions specially. */

	    action = h->actions[cached_retval];
	}

	D((stderr,
	   "use_cached_chain=%d action=%d cached_retval=%d retval=%d\n",
	   use_cached_chain, action, cached_retval, retval));

	/* decide what to do */
	switch (action) {
	case _PAM_ACTION_RESET:

	    /* if (use_cached_chain) {
	           XXX - we need to consider the use_cached_chain case
      	                 do we want to trash accumulated info here..?
	       } */

	    impression = _PAM_UNDEF;
	    status = PAM_MUST_FAIL_CODE;
	    break;

	case _PAM_ACTION_OK:
	case _PAM_ACTION_DONE:

	    /* XXX - should we maintain cached_status and status in
               the case of use_cached_chain? The same with BAD&DIE
               below */

	    if ( impression == _PAM_UNDEF
		 || (impression == _PAM_POSITIVE && status == PAM_SUCCESS) ) {
		impression = _PAM_POSITIVE;
		status = retval;
	    }
	    if ( impression == _PAM_POSITIVE && action == _PAM_ACTION_DONE ) {
		goto decision_made;
	    }
	    break;

	case _PAM_ACTION_BAD:
	case _PAM_ACTION_DIE:
#ifdef PAM_FAIL_NOW_ON
	    if ( cached_retval == PAM_ABORT ) {
		impression = _PAM_NEGATIVE;
		status = PAM_PERM_DENIED;
		goto decision_made;
	    }
#endif /* PAM_FAIL_NOW_ON */
	    if ( impression != _PAM_NEGATIVE ) {
		impression = _PAM_NEGATIVE;
		status = retval;
	    }
	    if ( action == _PAM_ACTION_DIE ) {
		goto decision_made;
	    }
	    break;

	case _PAM_ACTION_IGNORE:
	    /* if (use_cached_chain) {
	            XXX - when evaluating a cached
	            chain, do we still want to ignore the module's
	            return value?
	       } */
	    break;

        /* if we get here, we expect action is a positive number --
           this is what the ...JUMP macro checks. */

	default:
	    if ( _PAM_ACTION_IS_JUMP(action) ) {

		/* If we are evaluating a cached chain, we treat this
		   module as required (aka _PAM_ACTION_OK) as well as
		   executing the jump. */

		if (use_cached_chain) {
		    if (impression == _PAM_UNDEF
			|| (impression == _PAM_POSITIVE
			    && status == PAM_SUCCESS) ) {
			impression = _PAM_POSITIVE;
			status = retval;
		    }
		}
		
		/* this means that we need to skip #action stacked modules */
		do {
 		    h = h->next;
		} while ( --action > 0 && h != NULL );

		/* note if we try to skip too many modules action is
                   still non-zero and we snag the next if. */
	    }

	    /* this case is a syntax error: we can't succeed */
	    if (action) {
		D(("action syntax error"));
		impression = _PAM_NEGATIVE;
		status = PAM_MUST_FAIL_CODE;
	    }
	}
    }

decision_made:     /* by getting  here we have made a decision */

    /* Sanity check */
    if ( status == PAM_SUCCESS && impression != _PAM_POSITIVE ) {
	D(("caught on sanity check -- this is probably a config error!"));
	status = PAM_MUST_FAIL_CODE;
    }

    /* We have made a decision about the modules executed */
    return status;
}
Exemple #13
0
PAM_EXTERN int
pam_sm_authenticate (pam_handle_t * pamh,
		     int flags, int argc, const char **argv)
{
  int retval, rc;
  const char *user = NULL;
  const char *password = NULL;
  char otp[MAX_TOKEN_ID_LEN + TOKEN_OTP_LEN + 1] = { 0 };
  char otp_id[MAX_TOKEN_ID_LEN + 1] = { 0 };
  int password_len = 0;
  int skip_bytes = 0;
  int valid_token = 0;
  struct pam_conv *conv;
  struct pam_message *pmsg[1], msg[1];
  struct pam_response *resp;
  int nargs = 1;
  ykclient_t *ykc = NULL;
  struct cfg cfg_st;
  struct cfg *cfg = &cfg_st; /* for DBG macro */

  parse_cfg (flags, argc, argv, cfg);

  retval = pam_get_user (pamh, &user, NULL);
  if (retval != PAM_SUCCESS)
    {
      DBG (("get user returned error: %s", pam_strerror (pamh, retval)));
      goto done;
    }
  DBG (("get user returned: %s", user));

  if (cfg->mode == CHRESP) {
    return do_challenge_response(pamh, cfg, user);
  }

  if (cfg->try_first_pass || cfg->use_first_pass)
    {
      retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &password);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("get password returned error: %s",
	      pam_strerror (pamh, retval)));
	  goto done;
	}
      DBG (("get password returned: %s", password));
    }

  if (cfg->use_first_pass && password == NULL)
    {
      DBG (("use_first_pass set and no password, giving up"));
      retval = PAM_AUTH_ERR;
      goto done;
    }

  rc = ykclient_init (&ykc);
  if (rc != YKCLIENT_OK)
    {
      DBG (("ykclient_init() failed (%d): %s", rc, ykclient_strerror (rc)));
      retval = PAM_AUTHINFO_UNAVAIL;
      goto done;
    }

  rc = ykclient_set_client_b64 (ykc, cfg->client_id, cfg->client_key);
  if (rc != YKCLIENT_OK)
    {
      DBG (("ykclient_set_client_b64() failed (%d): %s",
	    rc, ykclient_strerror (rc)));
      retval = PAM_AUTHINFO_UNAVAIL;
      goto done;
    }

  if (cfg->capath)
    ykclient_set_ca_path (ykc, cfg->capath);

  if (cfg->url)
    ykclient_set_url_template (ykc, cfg->url);

  if (password == NULL)
    {
      retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("get conv returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}

      pmsg[0] = &msg[0];
      {
	const char *query_template = "Yubikey for `%s': ";
	size_t len = strlen (query_template) + strlen (user);
	size_t wrote;

	msg[0].msg = malloc (len);
	if (!msg[0].msg)
	  {
	    retval = PAM_BUF_ERR;
	    goto done;
	  }

	wrote = snprintf ((char *) msg[0].msg, len, query_template, user);
	if (wrote < 0 || wrote >= len)
	  {
	    retval = PAM_BUF_ERR;
	    goto done;
	  }
      }
      msg[0].msg_style = cfg->verbose_otp ? PAM_PROMPT_ECHO_ON : PAM_PROMPT_ECHO_OFF;
      resp = NULL;

      retval = conv->conv (nargs, (const struct pam_message **) pmsg,
			   &resp, conv->appdata_ptr);

      free ((char *) msg[0].msg);

      if (retval != PAM_SUCCESS)
	{
	  DBG (("conv returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}

      if (resp->resp == NULL)
	{
	  DBG (("conv returned NULL passwd?"));
	  goto done;
	}

      DBG (("conv returned %i bytes", strlen(resp->resp)));

      password = resp->resp;
    }

  password_len = strlen (password);
  if (password_len < (cfg->token_id_length + TOKEN_OTP_LEN))
    {
      DBG (("OTP too short to be considered : %i < %i", password_len, (cfg->token_id_length + TOKEN_OTP_LEN)));
      retval = PAM_AUTH_ERR;
      goto done;
    }

  /* In case the input was systempassword+YubiKeyOTP, we want to skip over
     "systempassword" when copying the token_id and OTP to separate buffers */
  skip_bytes = password_len - (cfg->token_id_length + TOKEN_OTP_LEN);

  DBG (("Skipping first %i bytes. Length is %i, token_id set to %i and token OTP always %i.",
	skip_bytes, password_len, cfg->token_id_length, TOKEN_OTP_LEN));

  /* Copy full YubiKey output (public ID + OTP) into otp */
  strncpy (otp, password + skip_bytes, sizeof (otp) - 1);
  /* Copy only public ID into otp_id. Destination buffer is zeroed. */
  strncpy (otp_id, password + skip_bytes, cfg->token_id_length);

  DBG (("OTP: %s ID: %s ", otp, otp_id));

  /* user entered their system password followed by generated OTP? */
  if (password_len > TOKEN_OTP_LEN + cfg->token_id_length)
    {
      char *onlypasswd = strdup (password);

      onlypasswd[password_len - (TOKEN_OTP_LEN + cfg->token_id_length)] = '\0';

      DBG (("Extracted a probable system password entered before the OTP - "
	    "setting item PAM_AUTHTOK"));

      retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd);
      free (onlypasswd);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("set_item returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}
    }
  else
    password = NULL;

  rc = ykclient_request (ykc, otp);

  DBG (("ykclient return value (%d): %s", rc,
	ykclient_strerror (rc)));

  switch (rc)
    {
    case YKCLIENT_OK:
      break;

    case YKCLIENT_BAD_OTP:
    case YKCLIENT_REPLAYED_OTP:
      retval = PAM_AUTH_ERR;
      goto done;

    default:
      retval = PAM_AUTHINFO_UNAVAIL;
      goto done;
    }

  /* authorize the user with supplied token id */
  if (cfg->ldapserver != NULL || cfg->ldap_uri != NULL)
    valid_token = authorize_user_token_ldap (cfg, user, otp_id);
  else
    valid_token = authorize_user_token (cfg, user, otp_id);

  if (valid_token == 0)
    {
      DBG (("Yubikey not authorized to login as user"));
      retval = PAM_AUTHINFO_UNAVAIL;
      goto done;
    }

  retval = PAM_SUCCESS;

done:
  if (ykc)
    ykclient_done (&ykc);
  if (cfg->alwaysok && retval != PAM_SUCCESS)
    {
      DBG (("alwaysok needed (otherwise return with %d)", retval));
      retval = PAM_SUCCESS;
    }
  DBG (("done. [%s]", pam_strerror (pamh, retval)));
  pam_set_data (pamh, "yubico_setcred_return", (void*) (intptr_t) retval, NULL);

  return retval;
}
Exemple #14
0
static CockpitCreds *
parse_auth_results (SessionLoginData *sl,
                    GHashTable *headers,
                    GError **error)
{
  CockpitCreds *creds = NULL;
  GByteArray *buffer;
  GError *json_error = NULL;
  const gchar *pam_user;
  JsonObject *results;
  gint64 code = -1;

  buffer = cockpit_pipe_get_buffer (sl->auth_pipe);
  g_debug ("cockpit-session says: %.*s", (int)buffer->len, (const gchar *)buffer->data);

  results = cockpit_json_parse_object ((const gchar *)buffer->data, buffer->len, &json_error);

  if (g_error_matches (json_error, JSON_PARSER_ERROR, JSON_PARSER_ERROR_INVALID_DATA))
    {
      g_message ("got non-utf8 user name from cockpit-session");
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
                   "Login user name is not UTF8 encoded");
      g_error_free (json_error);
    }
  else if (!results)
    {
      g_warning ("couldn't parse session auth output: %s",
                 json_error ? json_error->message : NULL);
      g_error_free (json_error);
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
                   "Invalid data from session process: no results");
    }
  else if (!cockpit_json_get_int (results, "result-code", -1, &code) || code < 0)
    {
      g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
                   "Invalid data from session process: bad PAM result");
    }
  else if (code == PAM_SUCCESS)
    {
      if (!cockpit_json_get_string (results, "user", NULL, &pam_user) || !pam_user)
        {
          g_set_error (error, G_IO_ERROR, G_IO_ERROR_INVALID_DATA,
                       "Invalid data from session process: missing user");
        }
      else
        {
          g_debug ("user authenticated as %s", pam_user);
          creds = create_creds_for_authenticated (pam_user, sl, results);
        }
    }
  else if (code == PAM_AUTH_ERR || code == PAM_USER_UNKNOWN)
    {
      g_debug ("authentication failed: %d", (int)code);
      g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_AUTHENTICATION_FAILED,
                   "Authentication failed");
    }
  else if (code == PAM_PERM_DENIED)
    {
      g_debug ("permission denied: %d", (int)code);
      g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_PERMISSION_DENIED,
                   "Permission denied");
    }
  else
    {
      g_debug ("pam error: %d", (int)code);
      g_set_error (error, COCKPIT_ERROR, COCKPIT_ERROR_FAILED,
                   "%s", pam_strerror (NULL, code));
    }

  build_gssapi_output_header (headers, results);

  if (results)
    json_object_unref (results);
  return creds;
}
Exemple #15
0
int pam_authenticate(pam_handle_t *pamh, int flags)
{
    int retval;

    D(("pam_authenticate called"));

    IF_NO_PAMH("pam_authenticate", pamh, PAM_SYSTEM_ERR);

    if (__PAM_FROM_MODULE(pamh)) {
	D(("called from module!?"));
	return PAM_SYSTEM_ERR;
    }

    if (pamh->former.choice == PAM_NOT_STACKED) {
	_pam_sanitize(pamh);
	_pam_start_timer(pamh);    /* we try to make the time for a failure
				      independent of the time it takes to
				      fail */
    }

    retval = _pam_dispatch(pamh, flags, PAM_AUTHENTICATE);

    if (retval != PAM_INCOMPLETE) {
	_pam_sanitize(pamh);
	_pam_await_timer(pamh, retval);   /* if unsuccessful then wait now */
	D(("pam_authenticate exit"));
    } else {
	D(("will resume when ready"));
    }
    	
#ifdef PRELUDE
    prelude_send_alert(pamh, retval);
#endif
     	
#if HAVE_LIBAUDIT
    retval = _pam_auditlog(pamh, PAM_AUTHENTICATE, retval, flags);
#endif

#ifdef CONFIG_PROP_STATSD_STATSD
	if (retval != PAM_SUCCESS) {

        char usr[MAX_PAM_STATS_USR_SIZE];
		char buf[MAX_PAM_STATS_BUF_SIZE]; 
		struct pam_data *data;
        char *u = NULL;

		/* The pam_sg module has stored module data so we 
		 * can tell whether this is a valid user. If not
		 * we log stats under "unknown". The proper mechanism
		 * for accessing module data bars access from within 
		 * application code so we are going around it. This is 
		 * a kludge, but the best one possible for now.
		 */
		data = pamh->data;
		while (data) {
			if ((!strcmp((data->name)+3, "null")) ||
			    (!strcmp((data->name)+3, pamh->user))) {
				u = (char *)(data->data);
				break;
			}
			data = data->next;
		}

		/* Don't log stats if the module info is unavailable
		 * or the PAM system itself failed during auth */
		if ((u != NULL) && strcmp(u, "PAM_SYSTEM_ERR")) {

			u = (!strcmp(u, "PAM_USER_UNKNOWN")) ? "unknown":pamh->user;

			usr[MAX_PAM_STATS_USR_SIZE-1]='\0';
			strncpy(usr,u,MAX_PAM_STATS_USR_SIZE-1);

			/* OK, start logging stats */
			memset(buf,'\0',MAX_PAM_STATS_BUF_SIZE);

			snprintf(buf, MAX_PAM_STATS_BUF_SIZE-1,
					"statsd -a incr pam_failed_%s %s \\;"
					         " push pam_last_failure_%s %s \"%s\" 0 \\;"
					         " incr pam_users %s \\; incr pam_services %s",
					usr,pamh->service_name,
					usr,pamh->service_name, pam_strerror(pamh, retval),
					usr,
					pamh->service_name);

			if (system(buf) == -1) {
				pam_syslog(pamh, LOG_INFO, "%s - failed", buf);
			}
		} 
	}
#endif

    return retval;
}
Exemple #16
0
static int
open_session (pam_handle_t *pamh)
{
  struct passwd *buf = NULL;
  const char *name;
  int res;

  name = NULL;
  pwd = NULL;

  res = pam_get_item (pamh, PAM_USER, (const void **)&name);
  if (res != PAM_SUCCESS)
    {
      warnx ("couldn't load user from pam");
      return res;
    }

  /* Yes, buf "leaks" */
  buf = malloc (sizeof (struct passwd) + 8192);
  if (buf == NULL)
    res = ENOMEM;
  else
    res = getpwnam_r (name, buf, (char *)(buf + 1), 8192, &pwd);
  if (pwd == NULL)
    {
      warnx ("couldn't load user info for: %s: %s", name,
             res == 0 ? "not found" : strerror (res));
      return PAM_SYSTEM_ERR;
    }

  /*
   * If we're already running as the right user, and have authenticated
   * then skip starting a new session. This is used when testing, or
   * running as your own user.
   */

  want_session = !(geteuid () != 0 &&
                   geteuid () == pwd->pw_uid &&
                   getuid () == pwd->pw_uid &&
                   getegid () == pwd->pw_gid &&
                   getgid () == pwd->pw_gid);

  if (want_session)
    {
      debug ("checking access for %s", name);
      res = pam_acct_mgmt (pamh, 0);
      if (res != PAM_SUCCESS)
        {
          warnx ("user account access failed: %s: %s", name, pam_strerror (pamh, res));

          /* We change PAM_AUTH_ERR to PAM_PERM_DENIED so that we can
           * distinguish between failures here and in *
           * pam_authenticate.
           */
          if (res == PAM_AUTH_ERR)
            res = PAM_PERM_DENIED;

          return res;
        }

      debug ("opening pam session for %s", name);

      pam_putenv (pamh, "XDG_SESSION_CLASS=user");

      res = pam_setcred (pamh, PAM_ESTABLISH_CRED);
      if (res != PAM_SUCCESS)
        {
          warnx ("establishing credentials failed: %s: %s", name, pam_strerror (pamh, res));
          return res;
        }

      res = pam_open_session (pamh, 0);
      if (res != PAM_SUCCESS)
        {
          warnx ("couldn't open session: %s: %s", name, pam_strerror (pamh, res));
          return res;
        }

      res = pam_setcred (pamh, PAM_REINITIALIZE_CRED);
      if (res != PAM_SUCCESS)
        {
          warnx ("reinitializing credentials failed: %s: %s", name, pam_strerror (pamh, res));
          return res;
        }
    }

  return PAM_SUCCESS;
}
Exemple #17
0
static void pam_dp_process_reply(DBusPendingCall *pending, void *ptr)
{
    DBusError dbus_error;
    DBusMessage* msg;
    int ret;
    int type;
    struct pam_auth_req *preq = NULL;
    struct pam_auth_dp_req *pdp_req;

    pdp_req = talloc_get_type(ptr, struct pam_auth_dp_req);
    preq = pdp_req->preq;
    talloc_free(pdp_req);

    dbus_error_init(&dbus_error);
    msg = dbus_pending_call_steal_reply(pending);

    /* Check if the client still exists. If not, simply free all the resources
     * and quit */
    if (preq == NULL) {
        DEBUG(SSSDBG_MINOR_FAILURE, "Client already disconnected\n");
        dbus_pending_call_unref(pending);
        dbus_message_unref(msg);
        return;
    }

    /* Sanity-check of message validity */
    if (msg == NULL) {
        DEBUG(SSSDBG_FATAL_FAILURE,
              "Severe error. A reply callback was called but no reply was"
                  "received and no timeout occurred\n");
        preq->pd->pam_status = PAM_SYSTEM_ERR;
        goto done;
    }

    type = dbus_message_get_type(msg);
    switch (type) {
        case DBUS_MESSAGE_TYPE_METHOD_RETURN:
            ret = dp_unpack_pam_response(msg, preq->pd, &dbus_error);
            if (!ret) {
                DEBUG(SSSDBG_FATAL_FAILURE, "Failed to parse reply.\n");
                preq->pd->pam_status = PAM_SYSTEM_ERR;
                goto done;
            }
            DEBUG(SSSDBG_FUNC_DATA,
                  "received: [%d (%s)][%s]\n", preq->pd->pam_status,
                  pam_strerror(NULL, preq->pd->pam_status),
                  preq->pd->domain);
             break;
        case DBUS_MESSAGE_TYPE_ERROR:
            DEBUG(SSSDBG_FATAL_FAILURE, "Reply error.\n");
            preq->pd->pam_status = PAM_SYSTEM_ERR;
            break;
        default:
            DEBUG(SSSDBG_FATAL_FAILURE, "Default... what now?.\n");
            preq->pd->pam_status = PAM_SYSTEM_ERR;
    }


done:
    dbus_pending_call_unref(pending);
    dbus_message_unref(msg);
    preq->callback(preq);
}
Exemple #18
0
static pam_handle_t *
perform_basic (void)
{
  struct pam_conv conv = { pam_conv_func, };
  pam_handle_t *pamh;
  char *input = NULL;
  char *password;
  int res;

  debug ("reading password from cockpit-ws");

  /* The input should be a user:password */
  input = read_fd_until_eof (AUTH_FD, "password", NULL);
  password = strchr (input, ':');
  if (password == NULL || strchr (password + 1, '\n'))
    {
      debug ("bad basic auth input");
      write_auth_begin (PAM_AUTH_ERR);
      write_auth_end ();
      exit (5);
    }

  *password = '******';
  password++;
  conv.appdata_ptr = &input;

  res = pam_start ("cockpit", input, &conv, &pamh);
  if (res != PAM_SUCCESS)
    errx (EX, "couldn't start pam: %s", pam_strerror (NULL, res));

  /* Move the password into place for use during auth */
  memmove (input, password, strlen (password) + 1);

  if (pam_set_item (pamh, PAM_RHOST, rhost) != PAM_SUCCESS)
    errx (EX, "couldn't setup pam");

  debug ("authenticating");

  res = pam_authenticate (pamh, 0);
  if (res == PAM_SUCCESS)
    res = open_session (pamh);

  write_auth_begin (res);
  if (res == PAM_SUCCESS && pwd)
    {
      write_auth_string ("user", pwd->pw_name);
      if (pwd->pw_gecos)
        write_auth_string ("full-name", pwd->pw_gecos);
    }
  write_auth_end ();

  if (res != PAM_SUCCESS)
    exit (5);

  if (input)
    {
      memset (input, 0, strlen (input));
      free (input);
    }

  return pamh;
}
PAM_EXTERN int
pam_sm_authenticate (pam_handle_t * pamh,
		     int flags, int argc, const char **argv)
{
  int retval, rc;
  const char *user = NULL;
  const char *password = NULL;
  char otp[MAX_OTP_LEN + 1];
  int password_len = 0;
  struct pam_conv *conv;
  struct pam_message *pmsg[1], msg[1];
  struct pam_response *resp;
  int nargs = 1;
  struct cfg cfg;
  char *query_prompt = NULL;
  char *onlypasswd = strdup ("");	/* empty passwords never match */

  parse_cfg (flags, argc, argv, &cfg);

  retval = pam_get_user (pamh, &user, NULL);
  if (retval != PAM_SUCCESS)
    {
      DBG (("get user returned error: %s", pam_strerror (pamh, retval)));
      goto done;
    }
  DBG (("get user returned: %s", user));

  if (cfg.try_first_pass || cfg.use_first_pass)
    {
      retval = pam_get_item (pamh, PAM_AUTHTOK, (const void **) &password);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("get password returned error: %s",
		pam_strerror (pamh, retval)));
	  goto done;
	}
      DBG (("get password returned: %s", password));
    }

  if (cfg.use_first_pass && password == NULL)
    {
      DBG (("use_first_pass set and no password, giving up"));
      retval = PAM_AUTH_ERR;
      goto done;
    }

  rc = oath_init ();
  if (rc != OATH_OK)
    {
      DBG (("oath_init() failed (%d)", rc));
      retval = PAM_AUTHINFO_UNAVAIL;
      goto done;
    }

  if (password == NULL)
    {
      retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("get conv returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}

      pmsg[0] = &msg[0];
      {
	const char *query_template = "One-time password (OATH) for `%s': ";
	size_t len = strlen (query_template) + strlen (user);
	size_t wrote;

	query_prompt = malloc (len);
	if (!query_prompt)
	  {
	    retval = PAM_BUF_ERR;
	    goto done;
	  }

	wrote = snprintf (query_prompt, len, query_template, user);
	if (wrote < 0 || wrote >= len)
	  {
	    retval = PAM_BUF_ERR;
	    goto done;
	  }

	msg[0].msg = query_prompt;
      }
      msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
      resp = NULL;

      retval = conv->conv (nargs, (const struct pam_message **) pmsg,
			   &resp, conv->appdata_ptr);

      free (query_prompt);
      query_prompt = NULL;

      if (retval != PAM_SUCCESS)
	{
	  DBG (("conv returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}

      DBG (("conv returned: %s", resp->resp));

      password = resp->resp;
    }

  if (password)
    password_len = strlen (password);
  else
    {
      DBG (("Could not read password"));
      retval = PAM_AUTH_ERR;
      goto done;
    }

  if (password_len < MIN_OTP_LEN)
    {
      DBG (("OTP too short: %s", password));
      retval = PAM_AUTH_ERR;
      goto done;
    }
  else if (cfg.digits != 0 && password_len < cfg.digits)
    {
      DBG (("OTP shorter than digits=%d: %s", cfg.digits, password));
      retval = PAM_AUTH_ERR;
      goto done;
    }
  else if (cfg.digits == 0 && password_len > MAX_OTP_LEN)
    {
      DBG (("OTP too long (and no digits=): %s", password));
      retval = PAM_AUTH_ERR;
      goto done;
    }
  else if (cfg.digits != 0 && password_len > cfg.digits)
    {
      free (onlypasswd);
      onlypasswd = strdup (password);

      /* user entered their system password followed by generated OTP? */

      onlypasswd[password_len - cfg.digits] = '\0';

      DBG (("Password: %s ", onlypasswd));

      memcpy (otp, password + password_len - cfg.digits, cfg.digits);
      otp[cfg.digits] = '\0';

      retval = pam_set_item (pamh, PAM_AUTHTOK, onlypasswd);
      if (retval != PAM_SUCCESS)
	{
	  DBG (("set_item returned error: %s", pam_strerror (pamh, retval)));
	  goto done;
	}
    }
  else
    {
      strcpy (otp, password);
      password = NULL;
    }

  DBG (("OTP: %s", otp ? otp : "(null)"));

  {
    time_t last_otp;

    rc = oath_authenticate_usersfile (cfg.usersfile,
				      user,
				      otp, cfg.window, onlypasswd, &last_otp);
    DBG (("authenticate rc %d (%s: %s) last otp %s", rc,
	  oath_strerror_name (rc) ? oath_strerror_name (rc) : "UNKNOWN",
	  oath_strerror (rc), ctime (&last_otp)));
  }

  if (rc != OATH_OK)
    {
      DBG (("One-time password not authorized to login as user '%s'", user));
      retval = PAM_AUTH_ERR;
      goto done;
    }

  retval = PAM_SUCCESS;

done:
  oath_done ();
  free (query_prompt);
  free (onlypasswd);
  if (cfg.alwaysok && retval != PAM_SUCCESS)
    {
      DBG (("alwaysok needed (otherwise return with %d)", retval));
      retval = PAM_SUCCESS;
    }
  DBG (("done. [%s]", pam_strerror (pamh, retval)));

  return retval;
}
Exemple #20
0
static pam_handle_t *
perform_gssapi (void)
{
  struct pam_conv conv = { pam_conv_func, };
  OM_uint32 major, minor;
  gss_cred_id_t client = GSS_C_NO_CREDENTIAL;
  gss_buffer_desc input = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc output = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc local = GSS_C_EMPTY_BUFFER;
  gss_buffer_desc display = GSS_C_EMPTY_BUFFER;
  gss_name_t name = GSS_C_NO_NAME;
  gss_ctx_id_t context = GSS_C_NO_CONTEXT;
  gss_OID mech_type = GSS_C_NO_OID;
  pam_handle_t *pamh = NULL;
  OM_uint32 flags = 0;
  char *str = NULL;
  OM_uint32 caps = 0;
  int res;

  res = PAM_AUTH_ERR;

  /* We shouldn't be writing to kerberos caches here */
  setenv ("KRB5CCNAME", "FILE:/dev/null", 1);

  debug ("reading kerberos auth from cockpit-ws");
  input.value = read_fd_until_eof (AUTH_FD, "gssapi data", &input.length);

  major = gss_accept_sec_context (&minor, &context, GSS_C_NO_CREDENTIAL, &input,
                                  GSS_C_NO_CHANNEL_BINDINGS, &name, &mech_type,
                                  &output, &flags, &caps, &client);

  if (GSS_ERROR (major))
    {
      warnx ("gssapi auth failed: %s", gssapi_strerror (major, minor));
      goto out;
    }

  /*
   * In general gssapi mechanisms can require multiple challenge response
   * iterations keeping &context between each, however Kerberos doesn't
   * require this, so we don't care :O
   *
   * If we ever want this to work with something other than Kerberos, then
   * we'll have to have some sorta session that holds the context.
   */
  if (major & GSS_S_CONTINUE_NEEDED)
    goto out;

  major = gss_localname (&minor, name, mech_type, &local);
  if (major == GSS_S_COMPLETE)
    {
      minor = 0;
      str = dup_string (local.value, local.length);
      debug ("mapped gssapi name to local user '%s'", str);

      if (getpwnam (str))
        {
          res = pam_start ("cockpit", str, &conv, &pamh);
        }
      else
        {
          /* If the local user doesn't exist, pretend gss_localname() failed */
          free (str);
          str = NULL;
          major = GSS_S_FAILURE;
          minor = KRB5_NO_LOCALNAME;
        }
    }

  if (major != GSS_S_COMPLETE)
    {
      if (minor == (OM_uint32)KRB5_NO_LOCALNAME || minor == (OM_uint32)KRB5_LNAME_NOTRANS)
        {
          major = gss_display_name (&minor, name, &display, NULL);
          if (GSS_ERROR (major))
            {
              warnx ("couldn't get gssapi display name: %s", gssapi_strerror (major, minor));
              goto out;
            }

          str = dup_string (display.value, display.length);
          debug ("no local user mapping for gssapi name '%s'", str);

          res = pam_start ("cockpit", str, &conv, &pamh);
        }
      else
        {
          warnx ("couldn't map gssapi name to local user: %s", gssapi_strerror (major, minor));
          goto out;
        }
    }

  if (res != PAM_SUCCESS)
    errx (EX, "couldn't start pam: %s", pam_strerror (NULL, res));

  if (pam_set_item (pamh, PAM_RHOST, rhost) != PAM_SUCCESS)
    errx (EX, "couldn't setup pam");

  res = open_session (pamh);

out:
  write_auth_begin (res);
  if (pwd)
    {
      write_auth_string ("user", pwd->pw_name);
      if (pwd->pw_gecos)
        write_auth_string ("full-name", pwd->pw_gecos);
    }
  if (output.value)
    write_auth_hex ("gssapi-output", output.value, output.length);

  if (output.value)
    gss_release_buffer (&minor, &output);
  output.value = NULL;
  output.length = 0;

  if (caps & GSS_C_DELEG_FLAG && client != GSS_C_NO_CREDENTIAL)
    {
#ifdef HAVE_GSS_IMPORT_CRED
      major = gss_export_cred (&minor, client, &output);
      if (GSS_ERROR (major))
        warnx ("couldn't export gssapi credentials: %s", gssapi_strerror (major, minor));
      else if (output.value)
        write_auth_hex ("gssapi-creds", output.value, output.length);
#else
      /* cockpit-ws will complain for us, if they're ever used */
      write_auth_hex ("gssapi-creds", (void *)"", 0);
#endif
    }

  write_auth_end ();

  if (display.value)
    gss_release_buffer (&minor, &display);
  if (output.value)
    gss_release_buffer (&minor, &output);
  if (local.value)
    gss_release_buffer (&minor, &local);
  if (client != GSS_C_NO_CREDENTIAL)
    gss_release_cred (&minor, &client);
  if (name != GSS_C_NO_NAME)
     gss_release_name (&minor, &name);
  if (context != GSS_C_NO_CONTEXT)
     gss_delete_sec_context (&minor, &context, GSS_C_NO_BUFFER);
  free (input.value);
  free (str);

  unsetenv ("KRB5CCNAME");

  if (res != PAM_SUCCESS)
    exit (5);

  return pamh;
}
Exemple #21
0
/*
 * main - groupmod command
 *
 */
int main (int argc, char **argv)
{
#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
	pam_handle_t *pamh = NULL;
	int retval;
#endif				/* USE_PAM */
#endif				/* ACCT_TOOLS_SETUID */

	/*
	 * Get my name so that I can use it to report errors.
	 */
	Prog = Basename (argv[0]);

	(void) setlocale (LC_ALL, "");
	(void) bindtextdomain (PACKAGE, LOCALEDIR);
	(void) textdomain (PACKAGE);

	process_root_flag ("-R", argc, argv);

	OPENLOG ("groupmod");
#ifdef WITH_AUDIT
	audit_help_open ();
#endif

	if (atexit (do_cleanups) != 0) {
		fprintf (stderr,
		         _("%s: Cannot setup cleanup service.\n"),
		         Prog);
		exit (1);
	}

	process_flags (argc, argv);

#ifdef ACCT_TOOLS_SETUID
#ifdef USE_PAM
	{
		struct passwd *pampw;
		pampw = getpwuid (getuid ()); /* local, no need for xgetpwuid */
		if (NULL == pampw) {
			fprintf (stderr,
			         _("%s: Cannot determine your user name.\n"),
			         Prog);
			exit (1);
		}

		retval = pam_start ("groupmod", pampw->pw_name, &conv, &pamh);
	}

	if (PAM_SUCCESS == retval) {
		retval = pam_authenticate (pamh, 0);
	}

	if (PAM_SUCCESS == retval) {
		retval = pam_acct_mgmt (pamh, 0);
	}

	if (PAM_SUCCESS != retval) {
		fprintf (stderr, _("%s: PAM: %s\n"),
		         Prog, pam_strerror (pamh, retval));
		SYSLOG((LOG_ERR, "%s", pam_strerror (pamh, retval)));
		if (NULL != pamh) {
			(void) pam_end (pamh, retval);
		}
		exit (1);
	}
	(void) pam_end (pamh, retval);
#endif				/* USE_PAM */
#endif				/* ACCT_TOOLS_SETUID */

#ifdef SHADOWGRP
	is_shadow_grp = sgr_file_present ();
#endif
	{
		struct group *grp;
		/*
		 * Start with a quick check to see if the group exists.
		 */
		grp = getgrnam (group_name); /* local, no need for xgetgrnam */
		if (NULL == grp) {
			fprintf (stderr,
			         _("%s: group '%s' does not exist\n"),
			         Prog, group_name);
			exit (E_NOTFOUND);
		} else {
			group_id = grp->gr_gid;
		}
	}

#ifdef	USE_NIS
	/*
	 * Now make sure it isn't an NIS group.
	 */
	if (__isgrNIS ()) {
		char *nis_domain;
		char *nis_master;

		fprintf (stderr,
		         _("%s: group %s is a NIS group\n"),
		         Prog, group_name);

		if (!yp_get_default_domain (&nis_domain) &&
		    !yp_master (nis_domain, "group.byname", &nis_master)) {
			fprintf (stderr,
			         _("%s: %s is the NIS master\n"),
			         Prog, nis_master);
		}
		exit (E_NOTFOUND);
	}
#endif

	if (gflg) {
		check_new_gid ();
	}

	if (nflg) {
		check_new_name ();
	}

	lock_files ();

	/*
	 * Now if the group is not changed, it's our fault.
	 * Make sure failures will be reported.
	 */
	prepare_failure_reports ();

	/*
	 * Do the hard stuff - open the files, create the group entries,
	 * then close and update the files.
	 */
	open_files ();

	grp_update ();

	close_files ();

	nscd_flush_cache ("group");

	return E_SUCCESS;
}
Exemple #22
0
int
main (int argc,
      char **argv)
{
  pam_handle_t *pamh = NULL;
  const char *auth;
  char **env;
  int status;
  int flags;
  int res;
  int i;

  if (isatty (0))
    errx (2, "this command is not meant to be run from the console");

  if (argc != 3)
    errx (2, "invalid arguments to cockpit-session");

  /* Cleanup the umask */
  umask (077);

  save_environment ();

  /* When setuid root, make sure our group is also root */
  if (geteuid () == 0)
    {
      /* Never trust the environment when running setuid() */
      if (getuid() != 0)
        {
          if (clearenv () != 0)
            err (1, "couldn't clear environment");
        }

      /* set a minimal environment */
      setenv ("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);

      if (setgid (0) != 0 || setuid (0) != 0)
        err (1, "couldn't switch permissions correctly");
    }

  /* We should never leak our auth fd to other processes */
  flags = fcntl (AUTH_FD, F_GETFD);
  if (flags < 0 || fcntl (AUTH_FD, F_SETFD, flags | FD_CLOEXEC))
    err (1, "couldn't set auth fd flags");

  auth = argv[1];
  rhost = argv[2];

  signal (SIGALRM, SIG_DFL);
  signal (SIGQUIT, SIG_DFL);
  signal (SIGTSTP, SIG_IGN);
  signal (SIGHUP, SIG_IGN);
  signal (SIGPIPE, SIG_IGN);

  if (strcmp (auth, "basic") == 0)
    pamh = perform_basic ();
  else if (strcmp (auth, "negotiate") == 0)
    pamh = perform_gssapi ();
  else
    errx (2, "unrecognized authentication method: %s", auth);

  for (i = 0; env_saved[i] != NULL; i++)
    pam_putenv (pamh, env_saved[i]);

  env = pam_getenvlist (pamh);
  if (env == NULL)
    errx (EX, "get pam environment failed");

  if (want_session)
    {
      assert (pwd != NULL);

      if (initgroups (pwd->pw_name, pwd->pw_gid) < 0)
        err (EX, "%s: can't init groups", pwd->pw_name);

      signal (SIGTERM, pass_to_child);
      signal (SIGINT, pass_to_child);
      signal (SIGQUIT, pass_to_child);

      utmp_log (1);

      status = fork_session (env);

      utmp_log (0);

      signal (SIGTERM, SIG_DFL);
      signal (SIGINT, SIG_DFL);
      signal (SIGQUIT, SIG_DFL);

      res = pam_setcred (pamh, PAM_DELETE_CRED);
      if (res != PAM_SUCCESS)
        err (EX, "%s: couldn't delete creds: %s", pwd->pw_name, pam_strerror (pamh, res));
      res = pam_close_session (pamh, 0);
      if (res != PAM_SUCCESS)
        err (EX, "%s: couldn't close session: %s", pwd->pw_name, pam_strerror (pamh, res));
    }
  else
    {
      status = session (env);
    }

  pam_end (pamh, PAM_SUCCESS);


  if (WIFEXITED(status))
    exit (WEXITSTATUS(status));
  else if (WIFSIGNALED(status))
    raise (WTERMSIG(status));
  else
    exit (127);
}
int
main (int argc, char *argv[])
{
  int rc;
  const char *user_to_auth;
  char *cookie = NULL;
  struct pam_conv pam_conversation;
  pam_handle_t *pam_h;
  const void *authed_user;

  rc = 0;
  pam_h = NULL;

  /* clear the entire environment to avoid attacks using with libraries honoring environment variables */
  if (_polkit_clearenv () != 0)
    goto error;

  /* set a minimal environment */
  setenv ("PATH", "/usr/sbin:/usr/bin:/sbin:/bin", 1);

  /* check that we are setuid root */
  if (geteuid () != 0)
    {
      gchar *s;

      fprintf (stderr, "polkit-agent-helper-1: needs to be setuid root\n");

      /* Special-case a very common error triggered in jhbuild setups */
      s = g_strdup_printf ("Incorrect permissions on %s (needs to be setuid root)", argv[0]);
      send_to_helper ("PAM_ERROR_MSG", s);
      g_free (s);
      goto error;
    }

  openlog ("polkit-agent-helper-1", LOG_CONS | LOG_PID, LOG_AUTHPRIV);

  /* check for correct invocation */
  if (!(argc == 2 || argc == 3))
    {
      syslog (LOG_NOTICE, "inappropriate use of helper, wrong number of arguments [uid=%d]", getuid ());
      fprintf (stderr, "polkit-agent-helper-1: wrong number of arguments. This incident has been logged.\n");
      goto error;
    }

  user_to_auth = argv[1];

  cookie = read_cookie (argc, argv);
  if (!cookie)
    goto error;

  if (getuid () != 0)
    {
      /* check we're running with a non-tty stdin */
      if (isatty (STDIN_FILENO) != 0)
        {
          syslog (LOG_NOTICE, "inappropriate use of helper, stdin is a tty [uid=%d]", getuid ());
          fprintf (stderr, "polkit-agent-helper-1: inappropriate use of helper, stdin is a tty. This incident has been logged.\n");
          goto error;
        }
    }

#ifdef PAH_DEBUG
  fprintf (stderr, "polkit-agent-helper-1: user to auth is '%s'.\n", user_to_auth);
#endif /* PAH_DEBUG */

  pam_conversation.conv        = conversation_function;
  pam_conversation.appdata_ptr = NULL;

  /* start the pam stack */
  rc = pam_start ("polkit-1",
                  user_to_auth,
                  &pam_conversation,
                  &pam_h);
  if (rc != PAM_SUCCESS)
    {
      fprintf (stderr, "polkit-agent-helper-1: pam_start failed: %s\n", pam_strerror (pam_h, rc));
      goto error;
    }

  /* set the requesting user */
  rc = pam_set_item (pam_h, PAM_RUSER, user_to_auth);
  if (rc != PAM_SUCCESS)
    {
      fprintf (stderr, "polkit-agent-helper-1: pam_set_item failed: %s\n", pam_strerror (pam_h, rc));
      goto error;
    }

  /* is user really user? */
  rc = pam_authenticate (pam_h, 0);
  if (rc != PAM_SUCCESS)
    {
      const char *err;
      err = pam_strerror (pam_h, rc);
      fprintf (stderr, "polkit-agent-helper-1: pam_authenticate failed: %s\n", err);
      goto error;
    }

  /* permitted access? */
  rc = pam_acct_mgmt (pam_h, 0);
  if (rc != PAM_SUCCESS)
    {
      const char *err;
      err = pam_strerror (pam_h, rc);
      fprintf (stderr, "polkit-agent-helper-1: pam_acct_mgmt failed: %s\n", err);
      goto error;
    }

  /* did we auth the right user? */
  rc = pam_get_item (pam_h, PAM_USER, &authed_user);
  if (rc != PAM_SUCCESS)
    {
      const char *err;
      err = pam_strerror (pam_h, rc);
      fprintf (stderr, "polkit-agent-helper-1: pam_get_item failed: %s\n", err);
      goto error;
    }

  if (strcmp (authed_user, user_to_auth) != 0)
    {
      fprintf (stderr, "polkit-agent-helper-1: Tried to auth user '%s' but we got auth for user '%s' instead",
               user_to_auth, (const char *) authed_user);
      goto error;
    }

#ifdef PAH_DEBUG
  fprintf (stderr, "polkit-agent-helper-1: successfully authenticated user '%s'.\n", user_to_auth);
#endif /* PAH_DEBUG */

  pam_end (pam_h, rc);
  pam_h = NULL;

#ifdef PAH_DEBUG
  fprintf (stderr, "polkit-agent-helper-1: sending D-Bus message to PolicyKit daemon\n");
#endif /* PAH_DEBUG */

  /* now send a D-Bus message to the PolicyKit daemon that
   * includes a) the cookie; and b) the user we authenticated
   */
  if (!send_dbus_message (cookie, user_to_auth))
    {
#ifdef PAH_DEBUG
      fprintf (stderr, "polkit-agent-helper-1: error sending D-Bus message to PolicyKit daemon\n");
#endif /* PAH_DEBUG */
      goto error;
    }

  free (cookie);

#ifdef PAH_DEBUG
  fprintf (stderr, "polkit-agent-helper-1: successfully sent D-Bus message to PolicyKit daemon\n");
#endif /* PAH_DEBUG */

  fprintf (stdout, "SUCCESS\n");
  flush_and_wait();
  return 0;

error:
  free (cookie);
  if (pam_h != NULL)
    pam_end (pam_h, rc);

  fprintf (stdout, "FAILURE\n");
  flush_and_wait();
  return 1;
}
static void
setup_child (int inp[2], int outp[2], int errp[2], pam_handle_t *ph, struct passwd *pwd)
{
	const char* display;
	int i, ret;

#ifdef VALGRIND 	
	char *args[] = { VALGRIND, VALGRIND_ARG, MATE_KEYRING_DAEMON, "--daemonize", "--login", NULL};
#else
	char *args[] = { MATE_KEYRING_DAEMON, "--daemonize", "--login", NULL};
#endif
	
	assert (pwd);
	assert (pwd->pw_dir);

	/* Fix up our end of the pipes */
	if (dup2 (inp[READ_END], STDIN) < 0 ||
	    dup2 (outp[WRITE_END], STDOUT) < 0 || 
	    dup2 (errp[WRITE_END], STDERR) < 0) {
	    	syslog (GKR_LOG_ERR, "gkr-pam: couldn't setup pipes: %s",
		        strerror (errno));
		exit (EXIT_FAILURE);
	}

	/* Try valiantly to close unnecessary file descriptors */
	for (i = STDERR; i < 64; ++i)
		close (i);
	    
	/* Close unnecessary file descriptors */
	close (inp[READ_END]);
	close (inp[WRITE_END]);
	close (outp[READ_END]);
	close (outp[WRITE_END]);
	close (errp[READ_END]);
	close (errp[WRITE_END]);
	
	/* We may be running effective as another user, revert that */
	seteuid (getuid ());
	setegid (getgid ());
	
	/* Setup process credentials */
	if (setgid (pwd->pw_gid) < 0 || setuid (pwd->pw_uid) < 0 ||
	    setegid (pwd->pw_gid) < 0 || seteuid (pwd->pw_uid) < 0) {
		syslog (GKR_LOG_ERR, "gkr-pam: couldn't setup credentials: %s", 
		        strerror (errno));
		exit (EXIT_FAILURE);
	}
	
	/* Setup environment variables */
	ret = setup_pam_env (ph, "HOME", pwd->pw_dir);
	if (ret == PAM_SUCCESS && !pam_getenv (ph, "DISPLAY")) {
		display = getenv ("DISPLAY");
		if (display)
			ret = setup_pam_env (ph, "DISPLAY", display);
	}
	
	/* Make sure that worked */
	if (ret != PAM_SUCCESS) {
		syslog (GKR_LOG_ERR, "gkr-pam: couldn't setup environment: %s", 
 		        pam_strerror (ph, ret));
 		exit (EXIT_FAILURE);
	}
	
	/* Now actually execute the process */
	execve (args[0], args, pam_getenvlist (ph));
	syslog (GKR_LOG_ERR, "gkr-pam: couldn't run mate-keyring-daemon: %s", 
	        strerror (errno));
	exit (EXIT_FAILURE);
}
Exemple #25
0
static int
sshpam_query(void *ctx, char **name, char **info,
    u_int *num, char ***prompts, u_int **echo_on)
{
	struct ssh *ssh = active_state; /* XXX */
	Buffer buffer;
	struct pam_ctxt *ctxt = ctx;
	size_t plen;
	u_char type;
	char *msg;
	size_t len, mlen;

	debug3("PAM: %s entering", __func__);
	buffer_init(&buffer);
	*name = xstrdup("");
	*info = xstrdup("");
	*prompts = xmalloc(sizeof(char *));
	**prompts = NULL;
	plen = 0;
	*echo_on = xmalloc(sizeof(u_int));
	while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) {
		type = buffer_get_char(&buffer);
		msg = buffer_get_string(&buffer, NULL);
		mlen = strlen(msg);
		switch (type) {
		case PAM_PROMPT_ECHO_ON:
		case PAM_PROMPT_ECHO_OFF:
			*num = 1;
			len = plen + mlen + 1;
			**prompts = xreallocarray(**prompts, 1, len);
			strlcpy(**prompts + plen, msg, len - plen);
			plen += mlen;
			**echo_on = (type == PAM_PROMPT_ECHO_ON);
			free(msg);
			return (0);
		case PAM_ERROR_MSG:
		case PAM_TEXT_INFO:
			/* accumulate messages */
			len = plen + mlen + 2;
			**prompts = xreallocarray(**prompts, 1, len);
			strlcpy(**prompts + plen, msg, len - plen);
			plen += mlen;
			strlcat(**prompts + plen, "\n", len - plen);
			plen++;
			free(msg);
			break;
		case PAM_ACCT_EXPIRED:
		case PAM_MAXTRIES:
			if (type == PAM_ACCT_EXPIRED)
				sshpam_account_status = 0;
			if (type == PAM_MAXTRIES)
				sshpam_set_maxtries_reached(1);
			/* FALLTHROUGH */
		case PAM_AUTH_ERR:
			debug3("PAM: %s", pam_strerror(sshpam_handle, type));
			if (**prompts != NULL && strlen(**prompts) != 0) {
				*info = **prompts;
				**prompts = NULL;
				*num = 0;
				**echo_on = 0;
				ctxt->pam_done = -1;
				free(msg);
				return 0;
			}
			/* FALLTHROUGH */
		case PAM_SUCCESS:
			if (**prompts != NULL) {
				/* drain any accumulated messages */
				debug("PAM: %s", **prompts);
				buffer_append(&loginmsg, **prompts,
				    strlen(**prompts));
				free(**prompts);
				**prompts = NULL;
			}
			if (type == PAM_SUCCESS) {
				if (!sshpam_authctxt->valid ||
				    (sshpam_authctxt->pw->pw_uid == 0 &&
				    options.permit_root_login != PERMIT_YES))
					fatal("Internal error: PAM auth "
					    "succeeded when it should have "
					    "failed");
				import_environments(&buffer);
				*num = 0;
				**echo_on = 0;
				ctxt->pam_done = 1;
				free(msg);
				return (0);
			}
			error("PAM: %s for %s%.100s from %.100s", msg,
			    sshpam_authctxt->valid ? "" : "illegal user ",
			    sshpam_authctxt->user,
			    auth_get_canonical_hostname(ssh, options.use_dns));
			/* FALLTHROUGH */
		default:
			*num = 0;
			**echo_on = 0;
			free(msg);
			ctxt->pam_done = -1;
			return (-1);
		}
	}
	return (-1);
}
PAM_EXTERN int
pam_sm_authenticate (pam_handle_t *ph, int unused, int argc, const char **argv)
{
	struct passwd *pwd;
	const char *user, *password;
	const char *control;
	int started_daemon;
	uint args;
	int ret;
	
	args = parse_args (ph, argc, argv);

	if (args & ARG_IGNORE_SERVICE)
		return PAM_SUCCESS;
		
	/* Figure out and/or prompt for the user name */
	ret = pam_get_user (ph, &user, NULL);
	if (ret != PAM_SUCCESS) {
		syslog (GKR_LOG_ERR, "gkr-pam: couldn't get the user name: %s", 
		        pam_strerror (ph, ret));
		return PAM_SERVICE_ERR;
	}
	
	pwd = getpwnam (user);
	if (!pwd) {
		syslog (GKR_LOG_ERR, "gkr-pam: error looking up user information");
		return PAM_SERVICE_ERR;
	}
		
	/* Look up the password */
	ret = pam_get_item (ph, PAM_AUTHTOK, (const void**)&password);
	if (ret != PAM_SUCCESS || password == NULL) {
		if (ret == PAM_SUCCESS)
			syslog (GKR_LOG_WARN, "gkr-pam: no password is available for user");
		else
			syslog (GKR_LOG_WARN, "gkr-pam: no password is available for user: %s", 
			        pam_strerror (ph, ret));
		return PAM_SUCCESS;
	}

	started_daemon = 0;

	/* Should we start the daemon? */
	if (args & ARG_AUTO_START) {
		ret = start_daemon_if_necessary (ph, pwd, password, &started_daemon);
		if (ret != PAM_SUCCESS)
			return ret;
	}

	control = get_any_env (ph, ENV_CONTROL);

	/* If mate keyring is running, then unlock now */
	if (control) {
		/* If we started the daemon, its already unlocked, since we passed the password */
		if (!started_daemon) {
			ret = unlock_keyring (ph, pwd, password);
			if (ret != PAM_SUCCESS)
				return ret;
		}
		
	/* Otherwise start later in open session, store password */
	} else {
		if (pam_set_data (ph, "gkr_system_authtok", strdup (password),
		                  cleanup_free_password) != PAM_SUCCESS) {
			syslog (GKR_LOG_ERR, "gkr-pam: error storing authtok");
			return PAM_AUTHTOK_RECOVER_ERR;
		}
 	}

	return PAM_SUCCESS;
}
struct passwd *
pam_modutil_getpwuid(pam_handle_t *pamh, uid_t uid)
{
#ifdef HAVE_GETPWUID_R

    void *buffer=NULL;
    size_t length = PWD_INITIAL_LENGTH;

    do {
	int status;
	void *new_buffer;
	struct passwd *result = NULL;

	new_buffer = realloc(buffer, sizeof(struct passwd) + length);
	if (new_buffer == NULL) {

	    D(("out of memory"));

	    /* no memory for the user - so delete the memory */
	    if (buffer) {
		free(buffer);
	    }
	    return NULL;
	}
	buffer = new_buffer;

	/* make the re-entrant call to get the pwd structure */
        errno = 0;
	status = getpwuid_r(uid, buffer,
			    sizeof(struct passwd) + (char *) buffer,
			    length, &result);
	if (!status && (result == buffer)) {
	    char *data_name;
	    const void *ignore;
	    int i;

	    data_name = malloc(strlen("_pammodutil_getpwuid") + 1 +
	    		       longlen((long) uid) + 1 + intlen(INT_MAX) + 1);
	    if ((pamh != NULL) && (data_name == NULL)) {
	        D(("was unable to register the data item [%s]",
	           pam_strerror(pamh, status)));
		free(buffer);
		return NULL;
	    }

	    if (pamh != NULL) {
	        for (i = 0; i < INT_MAX; i++) {
	            sprintf(data_name, "_pammodutil_getpwuid_%ld_%d",
		   	    (long) uid, i);
		    status = PAM_NO_MODULE_DATA;
	            if (pam_get_data(pamh, data_name, &ignore) != PAM_SUCCESS) {
		        status = pam_set_data(pamh, data_name,
					      result, pam_modutil_cleanup);
		    }
		    if (status == PAM_SUCCESS) {
		        break;
		    }
		}
	    } else {
	        status = PAM_SUCCESS;
	    }

	    free(data_name);

	    if (status == PAM_SUCCESS) {
		D(("success"));
		return result;
	    }

	    D(("was unable to register the data item [%s]",
	       pam_strerror(pamh, status)));

	    free(buffer);
	    return NULL;

	} else if (errno != ERANGE && errno != EINTR) {
                /* no sense in repeating the call */
                break;
        }
	
	length <<= PWD_LENGTH_SHIFT;

    } while (length < PWD_ABSURD_PWD_LENGTH);

    D(("pwd structure took %u bytes or so of memory",
       length+sizeof(struct passwd)));

    free(buffer);
    return NULL;

#else /* ie. ifndef HAVE_GETPWUID_R */

    /*
     * Sorry, there does not appear to be a reentrant version of
     * getpwuid(). So, we use the standard libc function.
     */
    
    return getpwuid(uid);

#endif /* def HAVE_GETPWUID_R */
}
Exemple #28
0
static int sftppam_driver_authenticate(sftp_kbdint_driver_t *driver,
    const char *user) {
  int res;

  pr_signals_block();
  PRIVS_ROOT

  res = pam_authenticate(sftppam_pamh, 0);
  if (res != PAM_SUCCESS) {
    switch (res) {
      case PAM_USER_UNKNOWN:
        sftppam_auth_code = PR_AUTH_NOPWD;
        break;

      default:
        sftppam_auth_code = PR_AUTH_BADPWD;
    }

    (void) pr_log_writefile(sftp_logfd, MOD_SFTP_PAM_VERSION,
      "PAM authentication error (%d) for user '%s': %s", res, user,
      pam_strerror(sftppam_pamh, res));
    (void) pr_log_pri(PR_LOG_NOTICE, MOD_SFTP_PAM_VERSION
      ": PAM authentication error (%d) for user '%s': %s", res, user,
      pam_strerror(sftppam_pamh, res));

    PRIVS_RELINQUISH
    pr_signals_unblock();

    errno = EPERM;
    return -1;
  }

  res = pam_acct_mgmt(sftppam_pamh, 0);
  if (res != PAM_SUCCESS) {
    switch (res) {
#ifdef PAM_AUTHTOKEN_REQD
      case PAM_AUTHTOKEN_REQD:
        pr_trace_msg(trace_channel, 8,
          "PAM account mgmt error: PAM_AUTHTOKEN_REQD");
        break;
#endif

      case PAM_ACCT_EXPIRED:
        pr_trace_msg(trace_channel, 8,
          "PAM account mgmt error: PAM_ACCT_EXPIRED");
        sftppam_auth_code = PR_AUTH_DISABLEDPWD;
        break;

#ifdef PAM_ACCT_DISABLED
      case PAM_ACCT_DISABLED:
        pr_trace_msg(trace_channel, 8,
          "PAM account mgmt error: PAM_ACCT_DISABLED");
        sftppam_auth_code = PR_AUTH_DISABLEDPWD;
        break;
#endif

      case PAM_USER_UNKNOWN:
        pr_trace_msg(trace_channel, 8,
          "PAM account mgmt error: PAM_USER_UNKNOWN");
        sftppam_auth_code = PR_AUTH_NOPWD;
        break;

      default:
        sftppam_auth_code = PR_AUTH_BADPWD;
        break;
    }

    pr_trace_msg(trace_channel, 1,
      "PAM account mgmt error (%d) for user '%s': %s", res, user,
      pam_strerror(sftppam_pamh, res));

    PRIVS_RELINQUISH
    pr_signals_unblock();

    errno = EPERM;
    return -1;
  }
 
  res = pam_open_session(sftppam_pamh, 0);
  if (res != PAM_SUCCESS) { 
    sftppam_auth_code = PR_AUTH_DISABLEDPWD;

    pr_trace_msg(trace_channel, 1,
      "PAM session error (%d) for user '%s': %s", res, user,
      pam_strerror(sftppam_pamh, res));

    PRIVS_RELINQUISH
    pr_signals_unblock();

    errno = EPERM;
    return -1;
  }

#ifdef PAM_CRED_ESTABLISH
  res = pam_setcred(sftppam_pamh, PAM_CRED_ESTABLISH);
#else
  res = pam_setcred(sftppam_pamh, PAM_ESTABLISH_CRED);
#endif /* !PAM_CRED_ESTABLISH */
  if (res != PAM_SUCCESS) {
    switch (res) {
      case PAM_CRED_EXPIRED:
        pr_trace_msg(trace_channel, 8,
          "PAM credentials error: PAM_CRED_EXPIRED");
        sftppam_auth_code = PR_AUTH_AGEPWD;
        break;

      case PAM_USER_UNKNOWN:
        pr_trace_msg(trace_channel, 8,
          "PAM credentials error: PAM_USER_UNKNOWN");
        sftppam_auth_code = PR_AUTH_NOPWD;
        break;

      default:
        sftppam_auth_code = PR_AUTH_BADPWD;
        break;
    }

    pr_trace_msg(trace_channel, 1,
      "PAM credentials error (%d) for user '%s': %s", res, user,
      pam_strerror(sftppam_pamh, res));

    PRIVS_RELINQUISH
    pr_signals_unblock();

    errno = EPERM;
    return -1;
  }

  /* XXX Not sure why these platforms have different treatment...? */
#if defined(SOLARIS2) || defined(HPUX10) || defined(HPUX11)
  res = pam_close_session(sftppam_pamh, 0);
  if (sftppam_pamh) {
    pam_end(sftppam_pamh, res);
    sftppam_pamh = NULL;
  }
#endif

  PRIVS_RELINQUISH
  pr_signals_unblock();

  return 0;
}
Exemple #29
0
static void
authenticate (const struct passwd *pw)
{
    const struct passwd *lpw = NULL;
    const char *cp, *srvname = NULL;
    int retval;

    switch (su_mode) {
    case SU_MODE:
        srvname = simulate_login ? PAM_SRVNAME_SU_L : PAM_SRVNAME_SU;
        break;
    case RUNUSER_MODE:
        srvname = simulate_login ? PAM_SRVNAME_RUNUSER_L : PAM_SRVNAME_RUNUSER;
        break;
    default:
        abort();
        break;
    }

    retval = pam_start (srvname, pw->pw_name, &conv, &pamh);
    if (is_pam_failure(retval))
        goto done;

    if (isatty (0) && (cp = ttyname (0)) != NULL)
    {
        const char *tty;

        if (strncmp (cp, "/dev/", 5) == 0)
            tty = cp + 5;
        else
            tty = cp;
        retval = pam_set_item (pamh, PAM_TTY, tty);
        if (is_pam_failure(retval))
            goto done;
    }

    lpw = current_getpwuid ();
    if (lpw && lpw->pw_name)
    {
        retval = pam_set_item (pamh, PAM_RUSER, (const void *) lpw->pw_name);
        if (is_pam_failure(retval))
            goto done;
    }

    if (su_mode == RUNUSER_MODE)
    {
        /*
         * This is the only difference between runuser(1) and su(1). The command
         * runuser(1) does not required authentication, because user is root.
         */
        if (restricted)
            errx(EXIT_FAILURE, _("may not be used by non-root users"));
        return;
    }

    retval = pam_authenticate (pamh, 0);
    if (is_pam_failure(retval))
        goto done;

    retval = pam_acct_mgmt (pamh, 0);
    if (retval == PAM_NEW_AUTHTOK_REQD)
    {
        /* Password has expired.  Offer option to change it.  */
        retval = pam_chauthtok (pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
    }

done:

    log_syslog(pw, !is_pam_failure(retval));

    if (is_pam_failure(retval))
    {
        const char *msg;

        log_btmp(pw);

        msg  = pam_strerror(pamh, retval);
        pam_end(pamh, retval);
        sleep (getlogindefs_num ("FAIL_DELAY", 1));
        errx (EXIT_FAILURE, "%s", msg?msg:_("incorrect password"));
    }
}
Exemple #30
0
int
start_stop_daemon(int argc, char **argv)
{
	int devnull_fd = -1;
#ifdef TIOCNOTTY
	int tty_fd = -1;
#endif

#ifdef HAVE_PAM
	pam_handle_t *pamh = NULL;
	int pamr;
	const char *const *pamenv = NULL;
#endif

	int opt;
	bool start = false;
	bool stop = false;
	bool oknodo = false;
	bool test = false;
	char *exec = NULL;
	char *startas = NULL;
	char *name = NULL;
	char *pidfile = NULL;
	char *retry = NULL;
	int sig = -1;
	int nicelevel = 0, ionicec = -1, ioniced = 0;
	bool background = false;
	bool makepidfile = false;
	bool interpreted = false;
	bool progress = false;
	uid_t uid = 0;
	gid_t gid = 0;
	char *home = NULL;
	int tid = 0;
	char *redirect_stderr = NULL;
	char *redirect_stdout = NULL;
	int stdout_fd;
	int stderr_fd;
	pid_t pid, spid;
	int i;
	char *svcname = getenv("RC_SVCNAME");
	RC_STRINGLIST *env_list;
	RC_STRING *env;
	char *tmp, *newpath, *np;
	char *p;
	char *token;
	char exec_file[PATH_MAX];
	struct passwd *pw;
	struct group *gr;
	char line[130];
	FILE *fp;
	size_t len;
	mode_t numask = 022;
	char **margv;
	unsigned int start_wait = 0;

	TAILQ_INIT(&schedule);
#ifdef DEBUG_MEMORY
	atexit(cleanup);
#endif

	signal_setup(SIGINT, handle_signal);
	signal_setup(SIGQUIT, handle_signal);
	signal_setup(SIGTERM, handle_signal);

	if ((tmp = getenv("SSD_NICELEVEL")))
		if (sscanf(tmp, "%d", &nicelevel) != 1)
			eerror("%s: invalid nice level `%s' (SSD_NICELEVEL)",
			    applet, tmp);

	/* Get our user name and initial dir */
	p = getenv("USER");
	home = getenv("HOME");
	if (home == NULL || p == NULL) {
		pw = getpwuid(getuid());
		if (pw != NULL) {
			if (p == NULL)
				setenv("USER", pw->pw_name, 1);
			if (home == NULL) {
				setenv("HOME", pw->pw_dir, 1);
				home = pw->pw_dir;
			}
		}
	}

	while ((opt = getopt_long(argc, argv, getoptstring, longopts,
		    (int *) 0)) != -1)
		switch (opt) {
		case 'I': /* --ionice */
			if (sscanf(optarg, "%d:%d", &ionicec, &ioniced) == 0)
				eerrorx("%s: invalid ionice `%s'",
				    applet, optarg);
			if (ionicec == 0)
				ioniced = 0;
			else if (ionicec == 3)
				ioniced = 7;
			ionicec <<= 13; /* class shift */
			break;

		case 'K':  /* --stop */
			stop = true;
			break;

		case 'N':  /* --nice */
			if (sscanf(optarg, "%d", &nicelevel) != 1)
				eerrorx("%s: invalid nice level `%s'",
				    applet, optarg);
			break;

		case 'P':  /* --progress */
			progress = true;
			break;

		case 'R':  /* --retry <schedule>|<timeout> */
			retry = optarg;
			break;

		case 'S':  /* --start */
			start = true;
			break;

		case 'b':  /* --background */
			background = true;
			break;

		case 'c':  /* --chuid <username>|<uid> */
			/* DEPRECATED */
			ewarn("WARNING: -c/--chuid is deprecated and will be removed in the future, please use -u/--user instead");
		case 'u':  /* --user <username>|<uid> */
		{
			p = optarg;
			tmp = strsep(&p, ":");
			changeuser = xstrdup(tmp);
			if (sscanf(tmp, "%d", &tid) != 1)
				pw = getpwnam(tmp);
			else
				pw = getpwuid((uid_t)tid);

			if (pw == NULL)
				eerrorx("%s: user `%s' not found",
				    applet, tmp);
			uid = pw->pw_uid;
			home = pw->pw_dir;
			unsetenv("HOME");
			if (pw->pw_dir)
				setenv("HOME", pw->pw_dir, 1);
			unsetenv("USER");
			if (pw->pw_name)
				setenv("USER", pw->pw_name, 1);
			if (gid == 0)
				gid = pw->pw_gid;

			if (p) {
				tmp = strsep (&p, ":");
				if (sscanf(tmp, "%d", &tid) != 1)
					gr = getgrnam(tmp);
				else
					gr = getgrgid((gid_t) tid);

				if (gr == NULL)
					eerrorx("%s: group `%s'"
					    " not found",
					    applet, tmp);
				gid = gr->gr_gid;
			}
		}
		break;

		case 'd':  /* --chdir /new/dir */
			ch_dir = optarg;
			break;

		case 'e': /* --env */
			putenv(optarg);
			break;

		case 'g':  /* --group <group>|<gid> */
			if (sscanf(optarg, "%d", &tid) != 1)
				gr = getgrnam(optarg);
			else
				gr = getgrgid((gid_t)tid);
			if (gr == NULL)
				eerrorx("%s: group `%s' not found",
				    applet, optarg);
			gid = gr->gr_gid;
			break;

		case 'i': /* --interpreted */
			interpreted = true;
			break;

		case 'k':
			if (parse_mode(&numask, optarg))
				eerrorx("%s: invalid mode `%s'",
				    applet, optarg);
			break;

		case 'm':  /* --make-pidfile */
			makepidfile = true;
			break;

		case 'n':  /* --name <process-name> */
			name = optarg;
			break;

		case 'o':  /* --oknodo */
			/* DEPRECATED */
			ewarn("WARNING: -o/--oknodo is deprecated and will be removed in the future");
			oknodo = true;
			break;

		case 'p':  /* --pidfile <pid-file> */
			pidfile = optarg;
			break;

		case 's':  /* --signal <signal> */
			sig = parse_signal(optarg);
			break;

		case 't':  /* --test */
			test = true;
			break;

		case 'r':  /* --chroot /new/root */
			ch_root = optarg;
			break;

		case 'a': /* --startas <name> */
			/* DEPRECATED */
			ewarn("WARNING: -a/--startas is deprecated and will be removed in the future, please use -x/--exec or -n/--name instead");
			startas = optarg;
			break;
		case 'w':
			if (sscanf(optarg, "%d", &start_wait) != 1)
				eerrorx("%s: `%s' not a number",
				    applet, optarg);
			break;
		case 'x':  /* --exec <executable> */
			exec = optarg;
			break;

		case '1':   /* --stdout /path/to/stdout.lgfile */
			redirect_stdout = optarg;
			break;

		case '2':  /* --stderr /path/to/stderr.logfile */
			redirect_stderr = optarg;
			break;

		case_RC_COMMON_GETOPT
		}

	endpwent();
	argc -= optind;
	argv += optind;

	/* Allow start-stop-daemon --signal HUP --exec /usr/sbin/dnsmasq
	 * instead of forcing --stop --oknodo as well */
	if (!start &&
	    !stop &&
	    sig != SIGINT &&
	    sig != SIGTERM &&
	    sig != SIGQUIT &&
	    sig != SIGKILL)
		oknodo = true;

	if (!exec)
		exec = startas;
	else if (!name)
		name = startas;

	if (!exec) {
		exec = *argv;
		if (!exec)
			exec = name;
		if (name && start)
			*argv = name;
	} else if (name)
		*--argv = name;
	else if (exec)
		*--argv = exec;

	if (stop || sig != -1) {
		if (sig == -1)
			sig = SIGTERM;
		if (!*argv && !pidfile && !name && !uid)
			eerrorx("%s: --stop needs --exec, --pidfile,"
			    " --name or --user", applet);
		if (background)
			eerrorx("%s: --background is only relevant with"
			    " --start", applet);
		if (makepidfile)
			eerrorx("%s: --make-pidfile is only relevant with"
			    " --start", applet);
		if (redirect_stdout || redirect_stderr)
			eerrorx("%s: --stdout and --stderr are only relevant"
			    " with --start", applet);
	} else {
		if (!exec)
			eerrorx("%s: nothing to start", applet);
		if (makepidfile && !pidfile)
			eerrorx("%s: --make-pidfile is only relevant with"
			    " --pidfile", applet);
		if ((redirect_stdout || redirect_stderr) && !background)
			eerrorx("%s: --stdout and --stderr are only relevant"
			    " with --background", applet);
	}

	/* Expand ~ */
	if (ch_dir && *ch_dir == '~')
		ch_dir = expand_home(home, ch_dir);
	if (ch_root && *ch_root == '~')
		ch_root = expand_home(home, ch_root);
	if (exec) {
		if (*exec == '~')
			exec = expand_home(home, exec);

		/* Validate that the binary exists if we are starting */
		if (*exec == '/' || *exec == '.') {
			/* Full or relative path */
			if (ch_root)
				snprintf(exec_file, sizeof(exec_file),
				    "%s/%s", ch_root, exec);
			else
				snprintf(exec_file, sizeof(exec_file),
				    "%s", exec);
		} else {
			/* Something in $PATH */
			p = tmp = xstrdup(getenv("PATH"));
			*exec_file = '\0';
			while ((token = strsep(&p, ":"))) {
				if (ch_root)
					snprintf(exec_file, sizeof(exec_file),
					    "%s/%s/%s",
					    ch_root, token, exec);
				else
					snprintf(exec_file, sizeof(exec_file),
					    "%s/%s", token, exec);
				if (exists(exec_file))
					break;
				*exec_file = '\0';
			}
			free(tmp);
		}
	}
	if (start && !exists(exec_file)) {
		eerror("%s: %s does not exist", applet,
		    *exec_file ? exec_file : exec);
		exit(EXIT_FAILURE);
	}

	/* If we don't have a pidfile we should check if it's interpreted
	 * or not. If it we, we need to pass the interpreter through
	 * to our daemon calls to find it correctly. */
	if (interpreted && !pidfile) {
		fp = fopen(exec_file, "r");
		if (fp) {
			p = fgets(line, sizeof(line), fp);
			fclose(fp);
			if (p != NULL && line[0] == '#' && line[1] == '!') {
				p = line + 2;
				/* Strip leading spaces */
				while (*p == ' ' || *p == '\t')
					p++;
				/* Remove the trailing newline */
				len = strlen(p) - 1;
				if (p[len] == '\n')
					p[len] = '\0';
				token = strsep(&p, " ");
				strncpy(exec_file, token, sizeof(exec_file));
				opt = 0;
				for (nav = argv; *nav; nav++)
					opt++;
				nav = xmalloc(sizeof(char *) * (opt + 3));
				nav[0] = exec_file;
				len = 1;
				if (p)
					nav[len++] = p;
				for (i = 0; i < opt; i++)
					nav[i + len] = argv[i];
				nav[i + len] = '\0';
			}
		}
	}
	margv = nav ? nav : argv;

	if (stop || sig != -1) {
		if (sig == -1)
			sig = SIGTERM;
		if (!stop)
			oknodo = true;
		if (retry)
			parse_schedule(retry, sig);
		else if (test || oknodo)
			parse_schedule("0", sig);
		else
			parse_schedule(NULL, sig);
		i = run_stop_schedule(exec, (const char *const *)margv,
		    pidfile, uid, test, progress);

		if (i < 0)
			/* We failed to stop something */
			exit(EXIT_FAILURE);
		if (test || oknodo)
			return i > 0 ? EXIT_SUCCESS : EXIT_FAILURE;

		/* Even if we have not actually killed anything, we should
		 * remove information about it as it may have unexpectedly
		 * crashed out. We should also return success as the end
		 * result would be the same. */
		if (pidfile && exists(pidfile))
			unlink(pidfile);
		if (svcname)
			rc_service_daemon_set(svcname, exec,
			    (const char *const *)argv,
			    pidfile, false);
		exit(EXIT_SUCCESS);
	}

	if (pidfile)
		pid = get_pid(pidfile);
	else
		pid = 0;

	if (do_stop(exec, (const char * const *)margv, pid, uid,
		0, test) > 0)
		eerrorx("%s: %s is already running", applet, exec);

	if (test) {
		if (rc_yesno(getenv("EINFO_QUIET")))
			exit (EXIT_SUCCESS);

		einfon("Would start");
		while (argc-- >= 0)
			printf(" %s", *argv++);
		printf("\n");
		eindent();
		if (uid != 0)
			einfo("as user id %d", uid);
		if (gid != 0)
			einfo("as group id %d", gid);
		if (ch_root)
			einfo("in root `%s'", ch_root);
		if (ch_dir)
			einfo("in dir `%s'", ch_dir);
		if (nicelevel != 0)
			einfo("with a priority of %d", nicelevel);
		if (name)
			einfo ("with a process name of %s", name);
		eoutdent();
		exit(EXIT_SUCCESS);
	}

	ebeginv("Detaching to start `%s'", exec);
	eindentv();

	/* Remove existing pidfile */
	if (pidfile)
		unlink(pidfile);

	if (background)
		signal_setup(SIGCHLD, handle_signal);

	if ((pid = fork()) == -1)
		eerrorx("%s: fork: %s", applet, strerror(errno));

	/* Child process - lets go! */
	if (pid == 0) {
		pid_t mypid = getpid();
		umask(numask);

#ifdef TIOCNOTTY
		tty_fd = open("/dev/tty", O_RDWR);
#endif

		devnull_fd = open("/dev/null", O_RDWR);

		if (nicelevel) {
			if (setpriority(PRIO_PROCESS, mypid, nicelevel) == -1)
				eerrorx("%s: setpritory %d: %s",
				    applet, nicelevel,
				    strerror(errno));
		}

		if (ionicec != -1 &&
		    ioprio_set(1, mypid, ionicec | ioniced) == -1)
			eerrorx("%s: ioprio_set %d %d: %s", applet,
			    ionicec, ioniced, strerror(errno));

		if (ch_root && chroot(ch_root) < 0)
			eerrorx("%s: chroot `%s': %s",
			    applet, ch_root, strerror(errno));

		if (ch_dir && chdir(ch_dir) < 0)
			eerrorx("%s: chdir `%s': %s",
			    applet, ch_dir, strerror(errno));

		if (makepidfile && pidfile) {
			fp = fopen(pidfile, "w");
			if (! fp)
				eerrorx("%s: fopen `%s': %s", applet, pidfile,
				    strerror(errno));
			fprintf(fp, "%d\n", mypid);
			fclose(fp);
		}

#ifdef HAVE_PAM
		if (changeuser != NULL) {
			pamr = pam_start("start-stop-daemon",
			    changeuser, &conv, &pamh);

			if (pamr == PAM_SUCCESS)
				pamr = pam_acct_mgmt(pamh, PAM_SILENT);
			if (pamr == PAM_SUCCESS)
				pamr = pam_open_session(pamh, PAM_SILENT);
			if (pamr != PAM_SUCCESS)
				eerrorx("%s: pam error: %s",
					applet, pam_strerror(pamh, pamr));
		}
#endif

		if (gid && setgid(gid))
			eerrorx("%s: unable to set groupid to %d",
			    applet, gid);
		if (changeuser && initgroups(changeuser, gid))
			eerrorx("%s: initgroups (%s, %d)",
			    applet, changeuser, gid);
		if (uid && setuid(uid))
			eerrorx ("%s: unable to set userid to %d",
			    applet, uid);

		/* Close any fd's to the passwd database */
		endpwent();

#ifdef TIOCNOTTY
		ioctl(tty_fd, TIOCNOTTY, 0);
		close(tty_fd);
#endif

		/* Clean the environment of any RC_ variables */
		env_list = rc_stringlist_new();
		i = 0;
		while (environ[i])
			rc_stringlist_add(env_list, environ[i++]);

#ifdef HAVE_PAM
		if (changeuser != NULL) {
			pamenv = (const char *const *)pam_getenvlist(pamh);
			if (pamenv) {
				while (*pamenv) {
					/* Don't add strings unless they set a var */
					if (strchr(*pamenv, '='))
						putenv(xstrdup(*pamenv));
					else
						unsetenv(*pamenv);
					pamenv++;
				}
			}
		}
#endif

		TAILQ_FOREACH(env, env_list, entries) {
			if ((strncmp(env->value, "RC_", 3) == 0 &&
				strncmp(env->value, "RC_SERVICE=", 10) != 0 &&
				strncmp(env->value, "RC_SVCNAME=", 10) != 0) ||
			    strncmp(env->value, "SSD_NICELEVEL=", 14) == 0)
			{
				p = strchr(env->value, '=');
				*p = '\0';
				unsetenv(env->value);
				continue;
			}
		}
		rc_stringlist_free(env_list);

		/* For the path, remove the rcscript bin dir from it */
		if ((token = getenv("PATH"))) {
			len = strlen(token);
			newpath = np = xmalloc(len + 1);
			while (token && *token) {
				p = strchr(token, ':');
				if (p) {
					*p++ = '\0';
					while (*p == ':')
						p++;
				}
				if (strcmp(token, RC_LIBEXECDIR "/bin") != 0 &&
				    strcmp(token, RC_LIBEXECDIR "/sbin") != 0)
				{
					len = strlen(token);
					if (np != newpath)
						*np++ = ':';
					memcpy(np, token, len);
					np += len;
				}
				token = p;
			}
			*np = '\0';
			unsetenv("PATH");
			setenv("PATH", newpath, 1);
		}

		stdout_fd = devnull_fd;
		stderr_fd = devnull_fd;
		if (redirect_stdout) {
			if ((stdout_fd = open(redirect_stdout,
				    O_WRONLY | O_CREAT | O_APPEND,
				    S_IRUSR | S_IWUSR)) == -1)
				eerrorx("%s: unable to open the logfile"
				    " for stdout `%s': %s",
				    applet, redirect_stdout, strerror(errno));
		}
		if (redirect_stderr) {
			if ((stderr_fd = open(redirect_stderr,
				    O_WRONLY | O_CREAT | O_APPEND,
				    S_IRUSR | S_IWUSR)) == -1)
				eerrorx("%s: unable to open the logfile"
				    " for stderr `%s': %s",
				    applet, redirect_stderr, strerror(errno));
		}

		/* We don't redirect stdin as some daemons may need it */
		if (background || redirect_stdout || rc_yesno(getenv("EINFO_QUIET")))
			dup2(stdout_fd, STDOUT_FILENO);
		if (background || redirect_stderr || rc_yesno(getenv("EINFO_QUIET")))
			dup2(stderr_fd, STDERR_FILENO);

		for (i = getdtablesize() - 1; i >= 3; --i)
			close(i);

		setsid();
		execvp(exec, argv);
#ifdef HAVE_PAM
		if (changeuser != NULL && pamr == PAM_SUCCESS)
			pam_close_session(pamh, PAM_SILENT);
#endif
		eerrorx("%s: failed to exec `%s': %s",
		    applet, exec,strerror(errno));
	}