17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate * CDDL HEADER START
37c478bd9Sstevel@tonic-gate *
47c478bd9Sstevel@tonic-gate * The contents of this file are subject to the terms of the
509295472Sgww * Common Development and Distribution License (the "License").
609295472Sgww * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate *
87c478bd9Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate * and limitations under the License.
127c478bd9Sstevel@tonic-gate *
137c478bd9Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate *
197c478bd9Sstevel@tonic-gate * CDDL HEADER END
207c478bd9Sstevel@tonic-gate */
217c478bd9Sstevel@tonic-gate /*
2206ccc4b8SMarek Pospisil * Copyright (c) 1988, 2010, Oracle and/or its affiliates. All rights reserved.
234ba5c7f8SMilan Jurik * Copyright 2012 Milan Jurik. All rights reserved.
2445405cceSAlexander Eremin * Copyright 2014 Nexenta Systems, Inc.
25*9e678d63SAndy Fiddaman * Copyright 2020 OmniOS Community Edition (OmniOSce) Association.
267c478bd9Sstevel@tonic-gate */
277c478bd9Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */
287c478bd9Sstevel@tonic-gate /* All Rights Reserved */
297c478bd9Sstevel@tonic-gate
307c478bd9Sstevel@tonic-gate /* Copyright (c) 1987, 1988 Microsoft Corporation */
317c478bd9Sstevel@tonic-gate /* All Rights Reserved */
327c478bd9Sstevel@tonic-gate
337c478bd9Sstevel@tonic-gate /*
347c478bd9Sstevel@tonic-gate * su [-] [name [arg ...]] change userid, `-' changes environment.
357c478bd9Sstevel@tonic-gate * If SULOG is defined, all attempts to su to another user are
367c478bd9Sstevel@tonic-gate * logged there.
377c478bd9Sstevel@tonic-gate * If CONSOLE is defined, all successful attempts to su to uid 0
387c478bd9Sstevel@tonic-gate * are also logged there.
397c478bd9Sstevel@tonic-gate *
407c478bd9Sstevel@tonic-gate * If su cannot create, open, or write entries into SULOG,
417c478bd9Sstevel@tonic-gate * (or on the CONSOLE, if defined), the entry will not
427c478bd9Sstevel@tonic-gate * be logged -- thus losing a record of the su's attempted
437c478bd9Sstevel@tonic-gate * during this period.
447c478bd9Sstevel@tonic-gate */
457c478bd9Sstevel@tonic-gate
467c478bd9Sstevel@tonic-gate #include <stdio.h>
477c478bd9Sstevel@tonic-gate #include <sys/types.h>
487c478bd9Sstevel@tonic-gate #include <sys/stat.h>
497c478bd9Sstevel@tonic-gate #include <sys/param.h>
507c478bd9Sstevel@tonic-gate #include <unistd.h>
517c478bd9Sstevel@tonic-gate #include <stdlib.h>
527c478bd9Sstevel@tonic-gate #include <crypt.h>
537c478bd9Sstevel@tonic-gate #include <pwd.h>
547c478bd9Sstevel@tonic-gate #include <shadow.h>
557c478bd9Sstevel@tonic-gate #include <time.h>
567c478bd9Sstevel@tonic-gate #include <signal.h>
577c478bd9Sstevel@tonic-gate #include <fcntl.h>
587c478bd9Sstevel@tonic-gate #include <string.h>
597c478bd9Sstevel@tonic-gate #include <locale.h>
607c478bd9Sstevel@tonic-gate #include <syslog.h>
617c478bd9Sstevel@tonic-gate #include <sys/utsname.h>
6209295472Sgww #include <sys/wait.h>
637c478bd9Sstevel@tonic-gate #include <grp.h>
647c478bd9Sstevel@tonic-gate #include <deflt.h>
657c478bd9Sstevel@tonic-gate #include <limits.h>
667c478bd9Sstevel@tonic-gate #include <errno.h>
677c478bd9Sstevel@tonic-gate #include <stdarg.h>
6809295472Sgww #include <user_attr.h>
6909295472Sgww #include <priv.h>
707c478bd9Sstevel@tonic-gate
715435d801Sgww #include <bsm/adt.h>
725435d801Sgww #include <bsm/adt_event.h>
735435d801Sgww
747c478bd9Sstevel@tonic-gate #include <security/pam_appl.h>
757c478bd9Sstevel@tonic-gate
767c478bd9Sstevel@tonic-gate #define PATH "/usr/bin:" /* path for users other than root */
777c478bd9Sstevel@tonic-gate #define SUPATH "/usr/sbin:/usr/bin" /* path for root */
787c478bd9Sstevel@tonic-gate #define SUPRMT "PS1=# " /* primary prompt for root */
797c478bd9Sstevel@tonic-gate #define ELIM 128
807c478bd9Sstevel@tonic-gate #define ROOT 0
817c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
827c478bd9Sstevel@tonic-gate #define EMBEDDED_NAME "embedded_su"
835435d801Sgww #define DEF_ATTEMPTS 3 /* attempts to change password */
847c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
857c478bd9Sstevel@tonic-gate
865435d801Sgww #define PW_FALSE 1 /* no password change */
875435d801Sgww #define PW_TRUE 2 /* successful password change */
885435d801Sgww #define PW_FAILED 3 /* failed password change */
895435d801Sgww
907c478bd9Sstevel@tonic-gate /*
917c478bd9Sstevel@tonic-gate * Intervals to sleep after failed su
927c478bd9Sstevel@tonic-gate */
937c478bd9Sstevel@tonic-gate #ifndef SLEEPTIME
947c478bd9Sstevel@tonic-gate #define SLEEPTIME 4
957c478bd9Sstevel@tonic-gate #endif
967c478bd9Sstevel@tonic-gate
977c478bd9Sstevel@tonic-gate #define DEFAULT_LOGIN "/etc/default/login"
987c478bd9Sstevel@tonic-gate #define DEFFILE "/etc/default/su"
997c478bd9Sstevel@tonic-gate
1007c478bd9Sstevel@tonic-gate
1017c478bd9Sstevel@tonic-gate char *Sulog, *Console;
1027c478bd9Sstevel@tonic-gate char *Path, *Supath;
1037c478bd9Sstevel@tonic-gate
1047c478bd9Sstevel@tonic-gate /*
1057c478bd9Sstevel@tonic-gate * Locale variables to be propagated to "su -" environment
1067c478bd9Sstevel@tonic-gate */
1077c478bd9Sstevel@tonic-gate static char *initvar;
1087c478bd9Sstevel@tonic-gate static char *initenv[] = {
1097c478bd9Sstevel@tonic-gate "TZ", "LANG", "LC_CTYPE",
1107c478bd9Sstevel@tonic-gate "LC_NUMERIC", "LC_TIME", "LC_COLLATE",
1117c478bd9Sstevel@tonic-gate "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0};
1127c478bd9Sstevel@tonic-gate static char mail[30] = { "MAIL=/var/mail/" };
1137c478bd9Sstevel@tonic-gate
1147c478bd9Sstevel@tonic-gate static void envalt(void);
1155435d801Sgww static void log(char *, char *, int);
1165435d801Sgww static void to(int);
1177c478bd9Sstevel@tonic-gate
1187c478bd9Sstevel@tonic-gate enum messagemode { USAGE, ERR, WARN };
1195435d801Sgww static void message(enum messagemode, char *, ...);
1207c478bd9Sstevel@tonic-gate
1215435d801Sgww static char *alloc_vsprintf(const char *, va_list);
1225435d801Sgww static char *tail(char *);
1237c478bd9Sstevel@tonic-gate
124*9e678d63SAndy Fiddaman static void audit_success(int, struct passwd *, boolean_t);
12509295472Sgww static void audit_logout(adt_session_data_t *, au_event_t);
126*9e678d63SAndy Fiddaman static void audit_failure(int, struct passwd *, char *, int, boolean_t);
1277c478bd9Sstevel@tonic-gate
1287c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
129*9e678d63SAndy Fiddaman static void validate(char *, int *, boolean_t);
1305435d801Sgww static int legalenvvar(char *);
1317c478bd9Sstevel@tonic-gate static int su_conv(int, struct pam_message **, struct pam_response **, void *);
1327c478bd9Sstevel@tonic-gate static int emb_su_conv(int, struct pam_message **, struct pam_response **,
1337c478bd9Sstevel@tonic-gate void *);
1345435d801Sgww static void freeresponse(int, struct pam_response **response);
1357c478bd9Sstevel@tonic-gate static struct pam_conv pam_conv = {su_conv, NULL};
1367c478bd9Sstevel@tonic-gate static struct pam_conv emb_pam_conv = {emb_su_conv, NULL};
1375435d801Sgww static void quotemsg(char *, ...);
1387c478bd9Sstevel@tonic-gate static void readinitblock(void);
1395435d801Sgww #else /* !DYNAMIC_SU */
1405435d801Sgww static void update_audit(struct passwd *pwd);
1417c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
1427c478bd9Sstevel@tonic-gate
1435435d801Sgww static pam_handle_t *pamh = NULL; /* Authentication handle */
1447c478bd9Sstevel@tonic-gate struct passwd pwd;
1457c478bd9Sstevel@tonic-gate char pwdbuf[1024]; /* buffer for getpwnam_r() */
1467c478bd9Sstevel@tonic-gate char shell[] = "/usr/bin/sh"; /* default shell */
1477c478bd9Sstevel@tonic-gate char safe_shell[] = "/sbin/sh"; /* "fallback" shell */
1487c478bd9Sstevel@tonic-gate char su[PATH_MAX] = "su"; /* arg0 for exec of shprog */
1497c478bd9Sstevel@tonic-gate char homedir[PATH_MAX] = "HOME=";
1507c478bd9Sstevel@tonic-gate char logname[20] = "LOGNAME=";
1517c478bd9Sstevel@tonic-gate char *suprmt = SUPRMT;
1527c478bd9Sstevel@tonic-gate char termtyp[PATH_MAX] = "TERM=";
1537c478bd9Sstevel@tonic-gate char *term;
1547c478bd9Sstevel@tonic-gate char shelltyp[PATH_MAX] = "SHELL=";
1557c478bd9Sstevel@tonic-gate char *hz;
1567c478bd9Sstevel@tonic-gate char tznam[PATH_MAX];
1577c478bd9Sstevel@tonic-gate char hzname[10] = "HZ=";
1587c478bd9Sstevel@tonic-gate char path[PATH_MAX] = "PATH=";
1597c478bd9Sstevel@tonic-gate char supath[PATH_MAX] = "PATH=";
1607c478bd9Sstevel@tonic-gate char *envinit[ELIM];
1617c478bd9Sstevel@tonic-gate extern char **environ;
1627c478bd9Sstevel@tonic-gate char *ttyn;
1637c478bd9Sstevel@tonic-gate char *username; /* the invoker */
1647c478bd9Sstevel@tonic-gate static int dosyslog = 0; /* use syslog? */
1657c478bd9Sstevel@tonic-gate char *myname;
1667c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
16757c40785SJoep Vesseur int pam_flags = 0;
1687c478bd9Sstevel@tonic-gate boolean_t embedded = B_FALSE;
1697c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate int
main(int argc,char ** argv)1727c478bd9Sstevel@tonic-gate main(int argc, char **argv)
1737c478bd9Sstevel@tonic-gate {
1747c478bd9Sstevel@tonic-gate #ifndef DYNAMIC_SU
1757c478bd9Sstevel@tonic-gate struct spwd sp;
1767c478bd9Sstevel@tonic-gate char spbuf[1024]; /* buffer for getspnam_r() */
1777c478bd9Sstevel@tonic-gate char *password;
1785435d801Sgww #endif /* !DYNAMIC_SU */
1797c478bd9Sstevel@tonic-gate char *nptr;
1807c478bd9Sstevel@tonic-gate char *pshell;
1817c478bd9Sstevel@tonic-gate int eflag = 0;
1827c478bd9Sstevel@tonic-gate int envidx = 0;
1837c478bd9Sstevel@tonic-gate uid_t uid;
1847c478bd9Sstevel@tonic-gate gid_t gid;
1857c478bd9Sstevel@tonic-gate char *dir, *shprog, *name;
1867c478bd9Sstevel@tonic-gate char *ptr;
1877c478bd9Sstevel@tonic-gate char *prog = argv[0];
1887c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
1897c478bd9Sstevel@tonic-gate int sleeptime = SLEEPTIME;
1907c478bd9Sstevel@tonic-gate char **pam_env = 0;
1917c478bd9Sstevel@tonic-gate int flags = 0;
1927c478bd9Sstevel@tonic-gate int retcode;
1937c478bd9Sstevel@tonic-gate int idx = 0;
194*9e678d63SAndy Fiddaman userattr_t *user_entry;
195*9e678d63SAndy Fiddaman char *authname;
1967c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
1975435d801Sgww int pw_change = PW_FALSE;
198*9e678d63SAndy Fiddaman boolean_t isrole = B_FALSE;
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate (void) setlocale(LC_ALL, "");
2017c478bd9Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */
2027c478bd9Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */
2037c478bd9Sstevel@tonic-gate #endif
2047c478bd9Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN);
2057c478bd9Sstevel@tonic-gate
2067c478bd9Sstevel@tonic-gate myname = tail(argv[0]);
2077c478bd9Sstevel@tonic-gate
2085435d801Sgww #ifdef DYNAMIC_SU
2097c478bd9Sstevel@tonic-gate if (strcmp(myname, EMBEDDED_NAME) == 0) {
2107c478bd9Sstevel@tonic-gate embedded = B_TRUE;
2117c478bd9Sstevel@tonic-gate setbuf(stdin, NULL);
2127c478bd9Sstevel@tonic-gate setbuf(stdout, NULL);
2137c478bd9Sstevel@tonic-gate readinitblock();
2147c478bd9Sstevel@tonic-gate }
2155435d801Sgww #endif /* DYNAMIC_SU */
2167c478bd9Sstevel@tonic-gate
2177c478bd9Sstevel@tonic-gate if (argc > 1 && *argv[1] == '-') {
2187c478bd9Sstevel@tonic-gate /* Explicitly check for just `-' (no trailing chars) */
2197c478bd9Sstevel@tonic-gate if (strlen(argv[1]) == 1) {
2207c478bd9Sstevel@tonic-gate eflag++; /* set eflag if `-' is specified */
2217c478bd9Sstevel@tonic-gate argv++;
2227c478bd9Sstevel@tonic-gate argc--;
2237c478bd9Sstevel@tonic-gate } else {
2247c478bd9Sstevel@tonic-gate message(USAGE,
2257c478bd9Sstevel@tonic-gate gettext("Usage: %s [-] [ username [ arg ... ] ]"),
22686ecf0b4SJan Kryl prog);
2277c478bd9Sstevel@tonic-gate exit(1);
2287c478bd9Sstevel@tonic-gate }
2297c478bd9Sstevel@tonic-gate }
2307c478bd9Sstevel@tonic-gate
2317c478bd9Sstevel@tonic-gate /*
2327c478bd9Sstevel@tonic-gate * Determine specified userid, get their password file entry,
2337c478bd9Sstevel@tonic-gate * and set variables to values in password file entry fields.
2347c478bd9Sstevel@tonic-gate */
2357c478bd9Sstevel@tonic-gate if (argc > 1) {
2367c478bd9Sstevel@tonic-gate /*
2377c478bd9Sstevel@tonic-gate * Usernames can't start with a `-', so we check for that to
2387c478bd9Sstevel@tonic-gate * catch bad usage (like "su - -c ls").
2397c478bd9Sstevel@tonic-gate */
2407c478bd9Sstevel@tonic-gate if (*argv[1] == '-') {
2417c478bd9Sstevel@tonic-gate message(USAGE,
2427c478bd9Sstevel@tonic-gate gettext("Usage: %s [-] [ username [ arg ... ] ]"),
24386ecf0b4SJan Kryl prog);
2447c478bd9Sstevel@tonic-gate exit(1);
2457c478bd9Sstevel@tonic-gate } else
2467c478bd9Sstevel@tonic-gate nptr = argv[1]; /* use valid command-line username */
2477c478bd9Sstevel@tonic-gate } else
2487c478bd9Sstevel@tonic-gate nptr = "root"; /* use default "root" username */
2497c478bd9Sstevel@tonic-gate
2507c478bd9Sstevel@tonic-gate if (defopen(DEFFILE) == 0) {
2517c478bd9Sstevel@tonic-gate
252*9e678d63SAndy Fiddaman if ((Sulog = defread("SULOG=")) != NULL)
2537c478bd9Sstevel@tonic-gate Sulog = strdup(Sulog);
254*9e678d63SAndy Fiddaman if ((Console = defread("CONSOLE=")) != NULL)
2557c478bd9Sstevel@tonic-gate Console = strdup(Console);
256*9e678d63SAndy Fiddaman if ((Path = defread("PATH=")) != NULL)
2577c478bd9Sstevel@tonic-gate Path = strdup(Path);
258*9e678d63SAndy Fiddaman if ((Supath = defread("SUPATH=")) != NULL)
2597c478bd9Sstevel@tonic-gate Supath = strdup(Supath);
2607c478bd9Sstevel@tonic-gate if ((ptr = defread("SYSLOG=")) != NULL)
2617c478bd9Sstevel@tonic-gate dosyslog = strcmp(ptr, "YES") == 0;
2627c478bd9Sstevel@tonic-gate
2637c478bd9Sstevel@tonic-gate (void) defopen(NULL);
2647c478bd9Sstevel@tonic-gate }
2657c478bd9Sstevel@tonic-gate (void) strlcat(path, (Path) ? Path : PATH, sizeof (path));
2667c478bd9Sstevel@tonic-gate (void) strlcat(supath, (Supath) ? Supath : SUPATH, sizeof (supath));
2677c478bd9Sstevel@tonic-gate
2687c478bd9Sstevel@tonic-gate if ((ttyn = ttyname(0)) == NULL)
2697c478bd9Sstevel@tonic-gate if ((ttyn = ttyname(1)) == NULL)
2707c478bd9Sstevel@tonic-gate if ((ttyn = ttyname(2)) == NULL)
2717c478bd9Sstevel@tonic-gate ttyn = "/dev/???";
2727c478bd9Sstevel@tonic-gate if ((username = cuserid(NULL)) == NULL)
2737c478bd9Sstevel@tonic-gate username = "(null)";
2747c478bd9Sstevel@tonic-gate
2757c478bd9Sstevel@tonic-gate /*
2767c478bd9Sstevel@tonic-gate * if Sulog defined, create SULOG, if it does not exist, with
2777c478bd9Sstevel@tonic-gate * mode read/write user. Change owner and group to root
2787c478bd9Sstevel@tonic-gate */
2797c478bd9Sstevel@tonic-gate if (Sulog != NULL) {
2807c478bd9Sstevel@tonic-gate (void) close(open(Sulog, O_WRONLY | O_APPEND | O_CREAT,
2817c478bd9Sstevel@tonic-gate (S_IRUSR|S_IWUSR)));
2827c478bd9Sstevel@tonic-gate (void) chown(Sulog, (uid_t)ROOT, (gid_t)ROOT);
2837c478bd9Sstevel@tonic-gate }
2847c478bd9Sstevel@tonic-gate
2857c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
286*9e678d63SAndy Fiddaman authname = nptr;
287*9e678d63SAndy Fiddaman
288*9e678d63SAndy Fiddaman if (((user_entry = getusernam(authname)) != NULL)) {
289*9e678d63SAndy Fiddaman kva_t *attr = user_entry->attr;
290*9e678d63SAndy Fiddaman char *kv;
291*9e678d63SAndy Fiddaman
292*9e678d63SAndy Fiddaman if ((kv = kva_match(attr, USERATTR_TYPE_KW)) != NULL &&
293*9e678d63SAndy Fiddaman ((strcmp(kv, USERATTR_TYPE_NONADMIN_KW) == 0) ||
294*9e678d63SAndy Fiddaman (strcmp(kv, USERATTR_TYPE_ADMIN_KW) == 0))) {
295*9e678d63SAndy Fiddaman isrole = B_TRUE;
296*9e678d63SAndy Fiddaman
297*9e678d63SAndy Fiddaman if ((kv = kva_match(attr,
298*9e678d63SAndy Fiddaman USERATTR_ROLEAUTH_KW)) != NULL &&
299*9e678d63SAndy Fiddaman strcmp(kv, USERATTR_ROLEAUTH_USER) == 0) {
300*9e678d63SAndy Fiddaman authname = cuserid(NULL);
301*9e678d63SAndy Fiddaman }
302*9e678d63SAndy Fiddaman
303*9e678d63SAndy Fiddaman }
304*9e678d63SAndy Fiddaman free_userattr(user_entry);
305*9e678d63SAndy Fiddaman }
306*9e678d63SAndy Fiddaman
307*9e678d63SAndy Fiddaman if (authname == NULL)
308*9e678d63SAndy Fiddaman exit(1);
309*9e678d63SAndy Fiddaman
310*9e678d63SAndy Fiddaman if (pam_start(embedded ? EMBEDDED_NAME : "su", authname,
3117c478bd9Sstevel@tonic-gate embedded ? &emb_pam_conv : &pam_conv, &pamh) != PAM_SUCCESS)
3127c478bd9Sstevel@tonic-gate exit(1);
3137c478bd9Sstevel@tonic-gate if (pam_set_item(pamh, PAM_TTY, ttyn) != PAM_SUCCESS)
3147c478bd9Sstevel@tonic-gate exit(1);
31545405cceSAlexander Eremin if (getpwuid_r(getuid(), &pwd, pwdbuf, sizeof (pwdbuf)) == NULL ||
31645405cceSAlexander Eremin pam_set_item(pamh, PAM_AUSER, pwd.pw_name) != PAM_SUCCESS)
31745405cceSAlexander Eremin exit(1);
3187c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
3197c478bd9Sstevel@tonic-gate
3207c478bd9Sstevel@tonic-gate openlog("su", LOG_CONS, LOG_AUTH);
3217c478bd9Sstevel@tonic-gate
3227c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
3237c478bd9Sstevel@tonic-gate
3245435d801Sgww /*
3255435d801Sgww * Use the same value of sleeptime and password required that
3265435d801Sgww * login(1) uses.
3275435d801Sgww * This is obtained by reading the file /etc/default/login
3285435d801Sgww * using the def*() functions
3295435d801Sgww */
3305435d801Sgww if (defopen(DEFAULT_LOGIN) == 0) {
3315435d801Sgww if ((ptr = defread("SLEEPTIME=")) != NULL) {
3325435d801Sgww sleeptime = atoi(ptr);
3335435d801Sgww if (sleeptime < 0 || sleeptime > 5)
3345435d801Sgww sleeptime = SLEEPTIME;
3355435d801Sgww }
3365435d801Sgww
3375435d801Sgww if ((ptr = defread("PASSREQ=")) != NULL &&
3385435d801Sgww strcasecmp("YES", ptr) == 0)
33957c40785SJoep Vesseur pam_flags |= PAM_DISALLOW_NULL_AUTHTOK;
3405435d801Sgww
3415435d801Sgww (void) defopen((char *)NULL);
3425435d801Sgww }
3437c478bd9Sstevel@tonic-gate /*
3447c478bd9Sstevel@tonic-gate * Ignore SIGQUIT and SIGINT
3457c478bd9Sstevel@tonic-gate */
3467c478bd9Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_IGN);
3477c478bd9Sstevel@tonic-gate (void) signal(SIGINT, SIG_IGN);
3487c478bd9Sstevel@tonic-gate
3497c478bd9Sstevel@tonic-gate /* call pam_authenticate() to authenticate the user through PAM */
3507c478bd9Sstevel@tonic-gate if (getpwnam_r(nptr, &pwd, pwdbuf, sizeof (pwdbuf)) == NULL)
3517c478bd9Sstevel@tonic-gate retcode = PAM_USER_UNKNOWN;
3527c478bd9Sstevel@tonic-gate else if ((flags = (getuid() != (uid_t)ROOT)) != 0) {
35357c40785SJoep Vesseur retcode = pam_authenticate(pamh, pam_flags);
3547c478bd9Sstevel@tonic-gate } else /* root user does not need to authenticate */
3557c478bd9Sstevel@tonic-gate retcode = PAM_SUCCESS;
3567c478bd9Sstevel@tonic-gate
3577c478bd9Sstevel@tonic-gate if (retcode != PAM_SUCCESS) {
3587c478bd9Sstevel@tonic-gate /*
3595435d801Sgww * 1st step: audit and log the error.
3607c478bd9Sstevel@tonic-gate * 2nd step: sleep.
3617c478bd9Sstevel@tonic-gate * 3rd step: print out message to user.
3627c478bd9Sstevel@tonic-gate */
36309295472Sgww /* don't let audit_failure distinguish a role here */
364*9e678d63SAndy Fiddaman audit_failure(PW_FALSE, NULL, nptr, retcode, B_FALSE);
3657c478bd9Sstevel@tonic-gate switch (retcode) {
3667c478bd9Sstevel@tonic-gate case PAM_USER_UNKNOWN:
3677c478bd9Sstevel@tonic-gate closelog();
3687c478bd9Sstevel@tonic-gate (void) sleep(sleeptime);
3697c478bd9Sstevel@tonic-gate message(ERR, gettext("Unknown id: %s"), nptr);
3707c478bd9Sstevel@tonic-gate break;
3717c478bd9Sstevel@tonic-gate
3727c478bd9Sstevel@tonic-gate case PAM_AUTH_ERR:
3737c478bd9Sstevel@tonic-gate if (Sulog != NULL)
3747c478bd9Sstevel@tonic-gate log(Sulog, nptr, 0); /* log entry */
3757c478bd9Sstevel@tonic-gate if (dosyslog)
3767c478bd9Sstevel@tonic-gate syslog(LOG_CRIT, "'su %s' failed for %s on %s",
3777c478bd9Sstevel@tonic-gate pwd.pw_name, username, ttyn);
3787c478bd9Sstevel@tonic-gate closelog();
3797c478bd9Sstevel@tonic-gate (void) sleep(sleeptime);
3807c478bd9Sstevel@tonic-gate message(ERR, gettext("Sorry"));
3817c478bd9Sstevel@tonic-gate break;
3827c478bd9Sstevel@tonic-gate
3837c478bd9Sstevel@tonic-gate case PAM_CONV_ERR:
3847c478bd9Sstevel@tonic-gate default:
3857c478bd9Sstevel@tonic-gate if (dosyslog)
3867c478bd9Sstevel@tonic-gate syslog(LOG_CRIT, "'su %s' failed for %s on %s",
3877c478bd9Sstevel@tonic-gate pwd.pw_name, username, ttyn);
3887c478bd9Sstevel@tonic-gate closelog();
3897c478bd9Sstevel@tonic-gate (void) sleep(sleeptime);
3907c478bd9Sstevel@tonic-gate message(ERR, gettext("Sorry"));
3917c478bd9Sstevel@tonic-gate break;
3927c478bd9Sstevel@tonic-gate }
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_DFL);
3957c478bd9Sstevel@tonic-gate (void) signal(SIGINT, SIG_DFL);
3967c478bd9Sstevel@tonic-gate exit(1);
3977c478bd9Sstevel@tonic-gate }
3987c478bd9Sstevel@tonic-gate if (flags)
399*9e678d63SAndy Fiddaman validate(username, &pw_change, isrole);
4007c478bd9Sstevel@tonic-gate if (pam_setcred(pamh, PAM_REINITIALIZE_CRED) != PAM_SUCCESS) {
4017c478bd9Sstevel@tonic-gate message(ERR, gettext("unable to set credentials"));
4027c478bd9Sstevel@tonic-gate exit(2);
4037c478bd9Sstevel@tonic-gate }
4047c478bd9Sstevel@tonic-gate if (dosyslog)
40586ecf0b4SJan Kryl syslog(pwd.pw_uid == 0 ? LOG_NOTICE : LOG_INFO,
4067c478bd9Sstevel@tonic-gate "'su %s' succeeded for %s on %s",
4077c478bd9Sstevel@tonic-gate pwd.pw_name, username, ttyn);
4087c478bd9Sstevel@tonic-gate closelog();
4097c478bd9Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_DFL);
4107c478bd9Sstevel@tonic-gate (void) signal(SIGINT, SIG_DFL);
4115435d801Sgww #else /* !DYNAMIC_SU */
4127c478bd9Sstevel@tonic-gate if ((getpwnam_r(nptr, &pwd, pwdbuf, sizeof (pwdbuf)) == NULL) ||
4137c478bd9Sstevel@tonic-gate (getspnam_r(nptr, &sp, spbuf, sizeof (spbuf)) == NULL)) {
4147c478bd9Sstevel@tonic-gate message(ERR, gettext("Unknown id: %s"), nptr);
415*9e678d63SAndy Fiddaman audit_failure(PW_FALSE, NULL, nptr, PAM_USER_UNKNOWN, B_FALSE);
4167c478bd9Sstevel@tonic-gate closelog();
4177c478bd9Sstevel@tonic-gate exit(1);
4187c478bd9Sstevel@tonic-gate }
4197c478bd9Sstevel@tonic-gate
4207c478bd9Sstevel@tonic-gate /*
4217c478bd9Sstevel@tonic-gate * Prompt for password if invoking user is not root or
4227c478bd9Sstevel@tonic-gate * if specified(new) user requires a password
4237c478bd9Sstevel@tonic-gate */
4247c478bd9Sstevel@tonic-gate if (sp.sp_pwdp[0] == '\0' || getuid() == (uid_t)ROOT)
4257c478bd9Sstevel@tonic-gate goto ok;
4267c478bd9Sstevel@tonic-gate password = getpass(gettext("Password:"));
4277c478bd9Sstevel@tonic-gate
4287c478bd9Sstevel@tonic-gate if ((strcmp(sp.sp_pwdp, crypt(password, sp.sp_pwdp)) != 0)) {
4297c478bd9Sstevel@tonic-gate /* clear password file entry */
4307c478bd9Sstevel@tonic-gate (void) memset((void *)spbuf, 0, sizeof (spbuf));
4317c478bd9Sstevel@tonic-gate if (Sulog != NULL)
4327c478bd9Sstevel@tonic-gate log(Sulog, nptr, 0); /* log entry */
4337c478bd9Sstevel@tonic-gate message(ERR, gettext("Sorry"));
434*9e678d63SAndy Fiddaman audit_failure(PW_FALSE, NULL, nptr, PAM_AUTH_ERR, B_FALSE);
4357c478bd9Sstevel@tonic-gate if (dosyslog)
4367c478bd9Sstevel@tonic-gate syslog(LOG_CRIT, "'su %s' failed for %s on %s",
4377c478bd9Sstevel@tonic-gate pwd.pw_name, username, ttyn);
4387c478bd9Sstevel@tonic-gate closelog();
4397c478bd9Sstevel@tonic-gate exit(2);
4407c478bd9Sstevel@tonic-gate }
4417c478bd9Sstevel@tonic-gate /* clear password file entry */
4427c478bd9Sstevel@tonic-gate (void) memset((void *)spbuf, 0, sizeof (spbuf));
4437c478bd9Sstevel@tonic-gate ok:
4445435d801Sgww /* update audit session in a non-pam environment */
4455435d801Sgww update_audit(&pwd);
4467c478bd9Sstevel@tonic-gate if (dosyslog)
44786ecf0b4SJan Kryl syslog(pwd.pw_uid == 0 ? LOG_NOTICE : LOG_INFO,
4487c478bd9Sstevel@tonic-gate "'su %s' succeeded for %s on %s",
4497c478bd9Sstevel@tonic-gate pwd.pw_name, username, ttyn);
4505435d801Sgww #endif /* DYNAMIC_SU */
4517c478bd9Sstevel@tonic-gate
452*9e678d63SAndy Fiddaman audit_success(pw_change, &pwd, isrole);
4537c478bd9Sstevel@tonic-gate uid = pwd.pw_uid;
4547c478bd9Sstevel@tonic-gate gid = pwd.pw_gid;
4557c478bd9Sstevel@tonic-gate dir = strdup(pwd.pw_dir);
4567c478bd9Sstevel@tonic-gate shprog = strdup(pwd.pw_shell);
4577c478bd9Sstevel@tonic-gate name = strdup(pwd.pw_name);
4587c478bd9Sstevel@tonic-gate
4597c478bd9Sstevel@tonic-gate if (Sulog != NULL)
4607c478bd9Sstevel@tonic-gate log(Sulog, nptr, 1); /* log entry */
4617c478bd9Sstevel@tonic-gate
4627c478bd9Sstevel@tonic-gate /* set user and group ids to specified user */
4637c478bd9Sstevel@tonic-gate
4647c478bd9Sstevel@tonic-gate /* set the real (and effective) GID */
4657c478bd9Sstevel@tonic-gate if (setgid(gid) == -1) {
4667c478bd9Sstevel@tonic-gate message(ERR, gettext("Invalid GID"));
4677c478bd9Sstevel@tonic-gate exit(2);
4687c478bd9Sstevel@tonic-gate }
4697c478bd9Sstevel@tonic-gate /* Initialize the supplementary group access list. */
4707c478bd9Sstevel@tonic-gate if (!nptr)
4717c478bd9Sstevel@tonic-gate exit(2);
4727c478bd9Sstevel@tonic-gate if (initgroups(nptr, gid) == -1) {
4737c478bd9Sstevel@tonic-gate exit(2);
4747c478bd9Sstevel@tonic-gate }
4757c478bd9Sstevel@tonic-gate /* set the real (and effective) UID */
4767c478bd9Sstevel@tonic-gate if (setuid(uid) == -1) {
4777c478bd9Sstevel@tonic-gate message(ERR, gettext("Invalid UID"));
4787c478bd9Sstevel@tonic-gate exit(2);
4797c478bd9Sstevel@tonic-gate }
4807c478bd9Sstevel@tonic-gate
4817c478bd9Sstevel@tonic-gate /*
4827c478bd9Sstevel@tonic-gate * If new user's shell field is neither NULL nor equal to /usr/bin/sh,
4837c478bd9Sstevel@tonic-gate * set:
4847c478bd9Sstevel@tonic-gate *
4857c478bd9Sstevel@tonic-gate * pshell = their shell
4867c478bd9Sstevel@tonic-gate * su = [-]last component of shell's pathname
4877c478bd9Sstevel@tonic-gate *
4887c478bd9Sstevel@tonic-gate * Otherwise, set the shell to /usr/bin/sh and set argv[0] to '[-]su'.
4897c478bd9Sstevel@tonic-gate */
4907c478bd9Sstevel@tonic-gate if (shprog[0] != '\0' && strcmp(shell, shprog) != 0) {
4917c478bd9Sstevel@tonic-gate char *p;
4927c478bd9Sstevel@tonic-gate
4937c478bd9Sstevel@tonic-gate pshell = shprog;
4947c478bd9Sstevel@tonic-gate (void) strcpy(su, eflag ? "-" : "");
4957c478bd9Sstevel@tonic-gate
4967c478bd9Sstevel@tonic-gate if ((p = strrchr(pshell, '/')) != NULL)
4977c478bd9Sstevel@tonic-gate (void) strlcat(su, p + 1, sizeof (su));
4987c478bd9Sstevel@tonic-gate else
4997c478bd9Sstevel@tonic-gate (void) strlcat(su, pshell, sizeof (su));
5007c478bd9Sstevel@tonic-gate } else {
5017c478bd9Sstevel@tonic-gate pshell = shell;
5027c478bd9Sstevel@tonic-gate (void) strcpy(su, eflag ? "-su" : "su");
5037c478bd9Sstevel@tonic-gate }
5047c478bd9Sstevel@tonic-gate
5057c478bd9Sstevel@tonic-gate /*
5067c478bd9Sstevel@tonic-gate * set environment variables for new user;
5077c478bd9Sstevel@tonic-gate * arg0 for exec of shprog must now contain `-'
5087c478bd9Sstevel@tonic-gate * so that environment of new user is given
5097c478bd9Sstevel@tonic-gate */
5107c478bd9Sstevel@tonic-gate if (eflag) {
5117c478bd9Sstevel@tonic-gate int j;
5127c478bd9Sstevel@tonic-gate char *var;
5137c478bd9Sstevel@tonic-gate
5147c478bd9Sstevel@tonic-gate if (strlen(dir) == 0) {
5157c478bd9Sstevel@tonic-gate (void) strcpy(dir, "/");
5167c478bd9Sstevel@tonic-gate message(WARN, gettext("No directory! Using home=/"));
5177c478bd9Sstevel@tonic-gate }
5187c478bd9Sstevel@tonic-gate (void) strlcat(homedir, dir, sizeof (homedir));
5197c478bd9Sstevel@tonic-gate (void) strlcat(logname, name, sizeof (logname));
520*9e678d63SAndy Fiddaman if ((hz = getenv("HZ")) != NULL)
5217c478bd9Sstevel@tonic-gate (void) strlcat(hzname, hz, sizeof (hzname));
5227c478bd9Sstevel@tonic-gate
5237c478bd9Sstevel@tonic-gate (void) strlcat(shelltyp, pshell, sizeof (shelltyp));
5247c478bd9Sstevel@tonic-gate
5257c478bd9Sstevel@tonic-gate if (chdir(dir) < 0) {
5267c478bd9Sstevel@tonic-gate message(ERR, gettext("No directory!"));
5277c478bd9Sstevel@tonic-gate exit(1);
5287c478bd9Sstevel@tonic-gate }
5297c478bd9Sstevel@tonic-gate envinit[envidx = 0] = homedir;
5307c478bd9Sstevel@tonic-gate envinit[++envidx] = ((uid == (uid_t)ROOT) ? supath : path);
5317c478bd9Sstevel@tonic-gate envinit[++envidx] = logname;
5327c478bd9Sstevel@tonic-gate envinit[++envidx] = hzname;
5337c478bd9Sstevel@tonic-gate if ((term = getenv("TERM")) != NULL) {
5347c478bd9Sstevel@tonic-gate (void) strlcat(termtyp, term, sizeof (termtyp));
5357c478bd9Sstevel@tonic-gate envinit[++envidx] = termtyp;
5367c478bd9Sstevel@tonic-gate }
5377c478bd9Sstevel@tonic-gate envinit[++envidx] = shelltyp;
5387c478bd9Sstevel@tonic-gate
5397c478bd9Sstevel@tonic-gate (void) strlcat(mail, name, sizeof (mail));
5407c478bd9Sstevel@tonic-gate envinit[++envidx] = mail;
5417c478bd9Sstevel@tonic-gate
5427c478bd9Sstevel@tonic-gate /*
5437c478bd9Sstevel@tonic-gate * Fetch the relevant locale/TZ environment variables from
5447c478bd9Sstevel@tonic-gate * the inherited environment.
5457c478bd9Sstevel@tonic-gate *
5467c478bd9Sstevel@tonic-gate * We have a priority here for setting TZ. If TZ is set in
5477c478bd9Sstevel@tonic-gate * in the inherited environment, that value remains top
5487c478bd9Sstevel@tonic-gate * priority. If the file /etc/default/login has TIMEZONE set,
5497c478bd9Sstevel@tonic-gate * that has second highest priority.
5507c478bd9Sstevel@tonic-gate */
5517c478bd9Sstevel@tonic-gate tznam[0] = '\0';
5527c478bd9Sstevel@tonic-gate for (j = 0; initenv[j] != 0; j++) {
553*9e678d63SAndy Fiddaman if ((initvar = getenv(initenv[j])) != NULL) {
5547c478bd9Sstevel@tonic-gate
5557c478bd9Sstevel@tonic-gate /*
5567c478bd9Sstevel@tonic-gate * Skip over values beginning with '/' for
5577c478bd9Sstevel@tonic-gate * security.
5587c478bd9Sstevel@tonic-gate */
5597c478bd9Sstevel@tonic-gate if (initvar[0] == '/') continue;
5607c478bd9Sstevel@tonic-gate
5617c478bd9Sstevel@tonic-gate if (strcmp(initenv[j], "TZ") == 0) {
5627c478bd9Sstevel@tonic-gate (void) strcpy(tznam, "TZ=");
5637c478bd9Sstevel@tonic-gate (void) strlcat(tznam, initvar,
56486ecf0b4SJan Kryl sizeof (tznam));
5657c478bd9Sstevel@tonic-gate
5667c478bd9Sstevel@tonic-gate } else {
5677c478bd9Sstevel@tonic-gate var = (char *)
56886ecf0b4SJan Kryl malloc(strlen(initenv[j])
56986ecf0b4SJan Kryl + strlen(initvar)
57086ecf0b4SJan Kryl + 2);
5714ba5c7f8SMilan Jurik if (var == NULL) {
5724ba5c7f8SMilan Jurik perror("malloc");
5734ba5c7f8SMilan Jurik exit(4);
5744ba5c7f8SMilan Jurik }
5757c478bd9Sstevel@tonic-gate (void) strcpy(var, initenv[j]);
5767c478bd9Sstevel@tonic-gate (void) strcat(var, "=");
5777c478bd9Sstevel@tonic-gate (void) strcat(var, initvar);
5787c478bd9Sstevel@tonic-gate envinit[++envidx] = var;
5797c478bd9Sstevel@tonic-gate }
5807c478bd9Sstevel@tonic-gate }
5817c478bd9Sstevel@tonic-gate }
5827c478bd9Sstevel@tonic-gate
5837c478bd9Sstevel@tonic-gate /*
5847c478bd9Sstevel@tonic-gate * Check if TZ was found. If not then try to read it from
5857c478bd9Sstevel@tonic-gate * /etc/default/login.
5867c478bd9Sstevel@tonic-gate */
5877c478bd9Sstevel@tonic-gate if (tznam[0] == '\0') {
5887c478bd9Sstevel@tonic-gate if (defopen(DEFAULT_LOGIN) == 0) {
589*9e678d63SAndy Fiddaman if ((initvar = defread("TIMEZONE=")) != NULL) {
5907c478bd9Sstevel@tonic-gate (void) strcpy(tznam, "TZ=");
5917c478bd9Sstevel@tonic-gate (void) strlcat(tznam, initvar,
59286ecf0b4SJan Kryl sizeof (tznam));
5937c478bd9Sstevel@tonic-gate }
5947c478bd9Sstevel@tonic-gate (void) defopen(NULL);
5957c478bd9Sstevel@tonic-gate }
5967c478bd9Sstevel@tonic-gate }
5977c478bd9Sstevel@tonic-gate
5987c478bd9Sstevel@tonic-gate if (tznam[0] != '\0')
5997c478bd9Sstevel@tonic-gate envinit[++envidx] = tznam;
6007c478bd9Sstevel@tonic-gate
6017c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
6027c478bd9Sstevel@tonic-gate /*
6037c478bd9Sstevel@tonic-gate * set the PAM environment variables -
6047c478bd9Sstevel@tonic-gate * check for legal environment variables
6057c478bd9Sstevel@tonic-gate */
6067c478bd9Sstevel@tonic-gate if ((pam_env = pam_getenvlist(pamh)) != 0) {
6077c478bd9Sstevel@tonic-gate while (pam_env[idx] != 0) {
6087c478bd9Sstevel@tonic-gate if (envidx + 2 < ELIM &&
6097c478bd9Sstevel@tonic-gate legalenvvar(pam_env[idx])) {
6107c478bd9Sstevel@tonic-gate envinit[++envidx] = pam_env[idx];
6117c478bd9Sstevel@tonic-gate }
6127c478bd9Sstevel@tonic-gate idx++;
6137c478bd9Sstevel@tonic-gate }
6147c478bd9Sstevel@tonic-gate }
6155435d801Sgww #endif /* DYNAMIC_SU */
6167c478bd9Sstevel@tonic-gate envinit[++envidx] = NULL;
6177c478bd9Sstevel@tonic-gate environ = envinit;
6187c478bd9Sstevel@tonic-gate } else {
6197c478bd9Sstevel@tonic-gate char **pp = environ, **qq, *p;
6207c478bd9Sstevel@tonic-gate
6217c478bd9Sstevel@tonic-gate while ((p = *pp) != NULL) {
6227c478bd9Sstevel@tonic-gate if (*p == 'L' && p[1] == 'D' && p[2] == '_') {
6237c478bd9Sstevel@tonic-gate for (qq = pp; (*qq = qq[1]) != NULL; qq++)
6247c478bd9Sstevel@tonic-gate ;
6257c478bd9Sstevel@tonic-gate /* pp is not advanced */
6267c478bd9Sstevel@tonic-gate } else {
6277c478bd9Sstevel@tonic-gate pp++;
6287c478bd9Sstevel@tonic-gate }
6297c478bd9Sstevel@tonic-gate }
6307c478bd9Sstevel@tonic-gate }
6317c478bd9Sstevel@tonic-gate
6327c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
6337c478bd9Sstevel@tonic-gate if (pamh)
6347c478bd9Sstevel@tonic-gate (void) pam_end(pamh, PAM_SUCCESS);
6355435d801Sgww #endif /* DYNAMIC_SU */
6367c478bd9Sstevel@tonic-gate
6377c478bd9Sstevel@tonic-gate /*
6387c478bd9Sstevel@tonic-gate * if new user is root:
6397c478bd9Sstevel@tonic-gate * if CONSOLE defined, log entry there;
6407c478bd9Sstevel@tonic-gate * if eflag not set, change environment to that of root.
6417c478bd9Sstevel@tonic-gate */
6427c478bd9Sstevel@tonic-gate if (uid == (uid_t)ROOT) {
6437c478bd9Sstevel@tonic-gate if (Console != NULL)
6447c478bd9Sstevel@tonic-gate if (strcmp(ttyn, Console) != 0) {
6457c478bd9Sstevel@tonic-gate (void) signal(SIGALRM, to);
6467c478bd9Sstevel@tonic-gate (void) alarm(30);
6477c478bd9Sstevel@tonic-gate log(Console, nptr, 1);
6487c478bd9Sstevel@tonic-gate (void) alarm(0);
6497c478bd9Sstevel@tonic-gate }
6507c478bd9Sstevel@tonic-gate if (!eflag)
6517c478bd9Sstevel@tonic-gate envalt();
6527c478bd9Sstevel@tonic-gate }
6537c478bd9Sstevel@tonic-gate
6547c478bd9Sstevel@tonic-gate /*
6557c478bd9Sstevel@tonic-gate * Default for SIGCPU and SIGXFSZ. Shells inherit
6567c478bd9Sstevel@tonic-gate * signal disposition from parent. And the
6577c478bd9Sstevel@tonic-gate * shells should have default dispositions for these
6587c478bd9Sstevel@tonic-gate * signals.
6597c478bd9Sstevel@tonic-gate */
6607c478bd9Sstevel@tonic-gate (void) signal(SIGXCPU, SIG_DFL);
6617c478bd9Sstevel@tonic-gate (void) signal(SIGXFSZ, SIG_DFL);
6627c478bd9Sstevel@tonic-gate
6637c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
6647c478bd9Sstevel@tonic-gate if (embedded) {
6657c478bd9Sstevel@tonic-gate (void) puts("SUCCESS");
6667c478bd9Sstevel@tonic-gate /*
6677c478bd9Sstevel@tonic-gate * After this point, we're no longer talking the
6687c478bd9Sstevel@tonic-gate * embedded_su protocol, so turn it off.
6697c478bd9Sstevel@tonic-gate */
6707c478bd9Sstevel@tonic-gate embedded = B_FALSE;
6717c478bd9Sstevel@tonic-gate }
6727c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
6737c478bd9Sstevel@tonic-gate
6747c478bd9Sstevel@tonic-gate /*
6757c478bd9Sstevel@tonic-gate * if additional arguments, exec shell program with array
6767c478bd9Sstevel@tonic-gate * of pointers to arguments:
6777c478bd9Sstevel@tonic-gate * -> if shell = default, then su = [-]su
6787c478bd9Sstevel@tonic-gate * -> if shell != default, then su = [-]last component of
6797c478bd9Sstevel@tonic-gate * shell's pathname
6807c478bd9Sstevel@tonic-gate *
6817c478bd9Sstevel@tonic-gate * if no additional arguments, exec shell with arg0 of su
6827c478bd9Sstevel@tonic-gate * where:
6837c478bd9Sstevel@tonic-gate * -> if shell = default, then su = [-]su
6847c478bd9Sstevel@tonic-gate * -> if shell != default, then su = [-]last component of
6857c478bd9Sstevel@tonic-gate * shell's pathname
6867c478bd9Sstevel@tonic-gate */
6877c478bd9Sstevel@tonic-gate if (argc > 2) {
6887c478bd9Sstevel@tonic-gate argv[1] = su;
6897c478bd9Sstevel@tonic-gate (void) execv(pshell, &argv[1]);
6907c478bd9Sstevel@tonic-gate } else
6917c478bd9Sstevel@tonic-gate (void) execl(pshell, su, 0);
6927c478bd9Sstevel@tonic-gate
6937c478bd9Sstevel@tonic-gate
6947c478bd9Sstevel@tonic-gate /*
6957c478bd9Sstevel@tonic-gate * Try to clean up after an administrator who has made a mistake
6967c478bd9Sstevel@tonic-gate * configuring root's shell; if root's shell is other than /sbin/sh,
6977c478bd9Sstevel@tonic-gate * try exec'ing /sbin/sh instead.
6987c478bd9Sstevel@tonic-gate */
6997c478bd9Sstevel@tonic-gate if ((uid == (uid_t)ROOT) && (strcmp(name, "root") == 0) &&
7007c478bd9Sstevel@tonic-gate (strcmp(safe_shell, pshell) != 0)) {
7017c478bd9Sstevel@tonic-gate message(WARN,
7027c478bd9Sstevel@tonic-gate gettext("No shell %s. Trying fallback shell %s."),
7037c478bd9Sstevel@tonic-gate pshell, safe_shell);
7047c478bd9Sstevel@tonic-gate
7057c478bd9Sstevel@tonic-gate if (eflag) {
7067c478bd9Sstevel@tonic-gate (void) strcpy(su, "-sh");
7077c478bd9Sstevel@tonic-gate (void) strlcpy(shelltyp + strlen("SHELL="),
7087c478bd9Sstevel@tonic-gate safe_shell, sizeof (shelltyp) - strlen("SHELL="));
7097c478bd9Sstevel@tonic-gate } else {
7107c478bd9Sstevel@tonic-gate (void) strcpy(su, "sh");
7117c478bd9Sstevel@tonic-gate }
7127c478bd9Sstevel@tonic-gate
7137c478bd9Sstevel@tonic-gate if (argc > 2) {
7147c478bd9Sstevel@tonic-gate argv[1] = su;
7157c478bd9Sstevel@tonic-gate (void) execv(safe_shell, &argv[1]);
7167c478bd9Sstevel@tonic-gate } else {
7177c478bd9Sstevel@tonic-gate (void) execl(safe_shell, su, 0);
7187c478bd9Sstevel@tonic-gate }
7197c478bd9Sstevel@tonic-gate message(ERR, gettext("Couldn't exec fallback shell %s: %s"),
7207c478bd9Sstevel@tonic-gate safe_shell, strerror(errno));
7217c478bd9Sstevel@tonic-gate } else {
7227c478bd9Sstevel@tonic-gate message(ERR, gettext("No shell"));
7237c478bd9Sstevel@tonic-gate }
7247c478bd9Sstevel@tonic-gate return (3);
7257c478bd9Sstevel@tonic-gate }
7267c478bd9Sstevel@tonic-gate
7277c478bd9Sstevel@tonic-gate /*
7287c478bd9Sstevel@tonic-gate * Environment altering routine -
7297c478bd9Sstevel@tonic-gate * This routine is called when a user is su'ing to root
7307c478bd9Sstevel@tonic-gate * without specifying the - flag.
7317c478bd9Sstevel@tonic-gate * The user's PATH and PS1 variables are reset
7327c478bd9Sstevel@tonic-gate * to the correct value for root.
7337c478bd9Sstevel@tonic-gate * All of the user's other environment variables retain
7347c478bd9Sstevel@tonic-gate * their current values after the su (if they are exported).
7357c478bd9Sstevel@tonic-gate */
7367c478bd9Sstevel@tonic-gate static void
envalt(void)7377c478bd9Sstevel@tonic-gate envalt(void)
7387c478bd9Sstevel@tonic-gate {
7397c478bd9Sstevel@tonic-gate /*
7407c478bd9Sstevel@tonic-gate * If user has PATH variable in their environment, change its value
7417c478bd9Sstevel@tonic-gate * to /bin:/etc:/usr/bin ;
7427c478bd9Sstevel@tonic-gate * if user does not have PATH variable, add it to the user's
7437c478bd9Sstevel@tonic-gate * environment;
7447c478bd9Sstevel@tonic-gate * if either of the above fail, an error message is printed.
7457c478bd9Sstevel@tonic-gate */
7467c478bd9Sstevel@tonic-gate if (putenv(supath) != 0) {
7477c478bd9Sstevel@tonic-gate message(ERR,
7487c478bd9Sstevel@tonic-gate gettext("unable to obtain memory to expand environment"));
7497c478bd9Sstevel@tonic-gate exit(4);
7507c478bd9Sstevel@tonic-gate }
7517c478bd9Sstevel@tonic-gate
7527c478bd9Sstevel@tonic-gate /*
7537c478bd9Sstevel@tonic-gate * If user has PROMPT variable in their environment, change its value
7547c478bd9Sstevel@tonic-gate * to # ;
7557c478bd9Sstevel@tonic-gate * if user does not have PROMPT variable, add it to the user's
7567c478bd9Sstevel@tonic-gate * environment;
7577c478bd9Sstevel@tonic-gate * if either of the above fail, an error message is printed.
7587c478bd9Sstevel@tonic-gate */
7597c478bd9Sstevel@tonic-gate if (putenv(suprmt) != 0) {
7607c478bd9Sstevel@tonic-gate message(ERR,
7617c478bd9Sstevel@tonic-gate gettext("unable to obtain memory to expand environment"));
7627c478bd9Sstevel@tonic-gate exit(4);
7637c478bd9Sstevel@tonic-gate }
7647c478bd9Sstevel@tonic-gate }
7657c478bd9Sstevel@tonic-gate
7667c478bd9Sstevel@tonic-gate /*
7677c478bd9Sstevel@tonic-gate * Logging routine -
7687c478bd9Sstevel@tonic-gate * where = SULOG or CONSOLE
7697c478bd9Sstevel@tonic-gate * towho = specified user ( user being su'ed to )
7707c478bd9Sstevel@tonic-gate * how = 0 if su attempt failed; 1 if su attempt succeeded
7717c478bd9Sstevel@tonic-gate */
7727c478bd9Sstevel@tonic-gate static void
log(char * where,char * towho,int how)7737c478bd9Sstevel@tonic-gate log(char *where, char *towho, int how)
7747c478bd9Sstevel@tonic-gate {
7757c478bd9Sstevel@tonic-gate FILE *logf;
7767c478bd9Sstevel@tonic-gate time_t now;
7777c478bd9Sstevel@tonic-gate struct tm *tmp;
7787c478bd9Sstevel@tonic-gate
7797c478bd9Sstevel@tonic-gate /*
7807c478bd9Sstevel@tonic-gate * open SULOG or CONSOLE - if open fails, return
7817c478bd9Sstevel@tonic-gate */
7827c478bd9Sstevel@tonic-gate if ((logf = fopen(where, "a")) == NULL)
7837c478bd9Sstevel@tonic-gate return;
7847c478bd9Sstevel@tonic-gate
7857c478bd9Sstevel@tonic-gate now = time(0);
7867c478bd9Sstevel@tonic-gate tmp = localtime(&now);
7877c478bd9Sstevel@tonic-gate
7887c478bd9Sstevel@tonic-gate /*
7897c478bd9Sstevel@tonic-gate * write entry into SULOG or onto CONSOLE - if write fails, return
7907c478bd9Sstevel@tonic-gate */
7917c478bd9Sstevel@tonic-gate (void) fprintf(logf, "SU %.2d/%.2d %.2d:%.2d %c %s %s-%s\n",
7927c478bd9Sstevel@tonic-gate tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min,
7937c478bd9Sstevel@tonic-gate how ? '+' : '-', ttyn + sizeof ("/dev/") - 1, username, towho);
7947c478bd9Sstevel@tonic-gate
7957c478bd9Sstevel@tonic-gate (void) fclose(logf); /* close SULOG or CONSOLE */
7967c478bd9Sstevel@tonic-gate }
7977c478bd9Sstevel@tonic-gate
7987c478bd9Sstevel@tonic-gate /*ARGSUSED*/
7997c478bd9Sstevel@tonic-gate static void
to(int sig)8007c478bd9Sstevel@tonic-gate to(int sig)
8017c478bd9Sstevel@tonic-gate {}
8027c478bd9Sstevel@tonic-gate
8035435d801Sgww /*
8045435d801Sgww * audit_success - audit successful su
8055435d801Sgww *
8065435d801Sgww * Entry process audit context established -- i.e., pam_setcred()
8075435d801Sgww * or equivalent called.
8085435d801Sgww * pw_change = PW_TRUE, if successful password change audit
8095435d801Sgww * required.
8105435d801Sgww * pwd = passwd entry for new user.
8115435d801Sgww */
8125435d801Sgww
8135435d801Sgww static void
audit_success(int pw_change,struct passwd * pwd,boolean_t isrole)814*9e678d63SAndy Fiddaman audit_success(int pw_change, struct passwd *pwd, boolean_t isrole)
8155435d801Sgww {
8165435d801Sgww adt_session_data_t *ah = NULL;
8175435d801Sgww adt_event_data_t *event;
81809295472Sgww au_event_t event_id = ADT_su;
8195435d801Sgww
8205435d801Sgww if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
8215435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
8225435d801Sgww "adt_start_session(ADT_su): %m");
8235435d801Sgww return;
8245435d801Sgww }
825*9e678d63SAndy Fiddaman if (isrole)
82609295472Sgww event_id = ADT_role_login;
82709295472Sgww
8285435d801Sgww /* since proc uid/gid not yet updated */
8295435d801Sgww if (adt_set_user(ah, pwd->pw_uid, pwd->pw_gid, pwd->pw_uid,
8305435d801Sgww pwd->pw_gid, NULL, ADT_USER) != 0) {
8315435d801Sgww syslog(LOG_AUTH | LOG_ERR,
8325435d801Sgww "adt_set_user(ADT_su, ADT_FAILURE): %m");
8335435d801Sgww }
83409295472Sgww if ((event = adt_alloc_event(ah, event_id)) == NULL) {
8355435d801Sgww syslog(LOG_AUTH | LOG_ALERT, "adt_alloc_event(ADT_su): %m");
8365435d801Sgww } else if (adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS) != 0) {
8375435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
8385435d801Sgww "adt_put_event(ADT_su, ADT_SUCCESS): %m");
8395435d801Sgww }
8405435d801Sgww
8415435d801Sgww if (pw_change == PW_TRUE) {
8425435d801Sgww /* Also audit password change */
8435435d801Sgww adt_free_event(event);
8445435d801Sgww if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
8455435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
8465435d801Sgww "adt_alloc_event(ADT_passwd): %m");
8475435d801Sgww } else if (adt_put_event(event, ADT_SUCCESS,
8485435d801Sgww ADT_SUCCESS) != 0) {
8495435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
8505435d801Sgww "adt_put_event(ADT_passwd, ADT_SUCCESS): %m");
8515435d801Sgww }
8525435d801Sgww }
8535435d801Sgww adt_free_event(event);
85409295472Sgww /*
85509295472Sgww * The preceeding code is a noop if audit isn't enabled,
85609295472Sgww * but, let's not make a new process when it's not necessary.
85709295472Sgww */
85806ccc4b8SMarek Pospisil if (adt_audit_state(AUC_AUDITING)) {
859a98aba88Sgww audit_logout(ah, event_id); /* fork to catch logout */
86009295472Sgww }
86109295472Sgww (void) adt_end_session(ah);
86209295472Sgww }
86309295472Sgww
86409295472Sgww
86509295472Sgww /*
86609295472Sgww * audit_logout - audit successful su logout
86709295472Sgww *
86809295472Sgww * Entry ah = Successful su audit handle
86909295472Sgww * event_id = su event ID: ADT_su, ADT_role_login
87009295472Sgww *
87109295472Sgww * Exit Errors are just ignored and we go on.
87209295472Sgww * su logout event written.
87309295472Sgww */
87409295472Sgww static void
audit_logout(adt_session_data_t * ah,au_event_t event_id)87509295472Sgww audit_logout(adt_session_data_t *ah, au_event_t event_id)
87609295472Sgww {
87709295472Sgww adt_event_data_t *event;
87809295472Sgww int status; /* wait status */
87909295472Sgww pid_t pid;
88009295472Sgww priv_set_t *priv; /* waiting process privs */
88109295472Sgww
882a98aba88Sgww if (event_id == ADT_su) {
883a98aba88Sgww event_id = ADT_su_logout;
884a98aba88Sgww } else {
885a98aba88Sgww event_id = ADT_role_logout;
886a98aba88Sgww }
887a98aba88Sgww if ((event = adt_alloc_event(ah, event_id)) == NULL) {
888a98aba88Sgww syslog(LOG_AUTH | LOG_ALERT,
889a98aba88Sgww "adt_alloc_event(ADT_su_logout): %m");
89009295472Sgww return;
891a98aba88Sgww }
892a98aba88Sgww if ((priv = priv_allocset()) == NULL) {
893a98aba88Sgww syslog(LOG_AUTH | LOG_ALERT,
894634e26ecSCasper H.S. Dik "su audit_logout: could not alloc basic privs: %m");
895a98aba88Sgww adt_free_event(event);
89609295472Sgww return;
897a98aba88Sgww }
89809295472Sgww
899a98aba88Sgww /*
900a98aba88Sgww * The child returns and continues su processing.
901a98aba88Sgww * The parent's sole job is to wait for child exit, write the
902a98aba88Sgww * logout audit record, and replay the child's exit code.
903a98aba88Sgww */
904a98aba88Sgww if ((pid = fork()) == 0) {
905a98aba88Sgww /* child */
90609295472Sgww
90709295472Sgww adt_free_event(event);
908a98aba88Sgww priv_freeset(priv);
909a98aba88Sgww return;
91009295472Sgww }
911a98aba88Sgww if (pid == -1) {
912a98aba88Sgww /* failure */
913a98aba88Sgww
914a98aba88Sgww syslog(LOG_AUTH | LOG_ALERT,
915a98aba88Sgww "su audit_logout: could not fork: %m");
916a98aba88Sgww adt_free_event(event);
917a98aba88Sgww priv_freeset(priv);
918a98aba88Sgww return;
919a98aba88Sgww }
920a98aba88Sgww
921a98aba88Sgww /* parent process */
922a98aba88Sgww
923a98aba88Sgww /*
924a98aba88Sgww * When this routine is called, the current working
925a98aba88Sgww * directory is the unknown and there are unknown open
926a98aba88Sgww * files. For the waiting process, change the current
927a98aba88Sgww * directory to root and close open files so that
928a98aba88Sgww * directories can be unmounted if necessary.
929a98aba88Sgww */
930a98aba88Sgww if (chdir("/") != 0) {
931a98aba88Sgww syslog(LOG_AUTH | LOG_ALERT,
932a98aba88Sgww "su audit_logout: could not chdir /: %m");
933a98aba88Sgww }
934a98aba88Sgww /*
935a98aba88Sgww * Reduce privileges to just those needed.
936a98aba88Sgww */
937634e26ecSCasper H.S. Dik priv_basicset(priv);
938634e26ecSCasper H.S. Dik (void) priv_delset(priv, PRIV_PROC_EXEC);
939634e26ecSCasper H.S. Dik (void) priv_delset(priv, PRIV_PROC_FORK);
940634e26ecSCasper H.S. Dik (void) priv_delset(priv, PRIV_PROC_INFO);
941634e26ecSCasper H.S. Dik (void) priv_delset(priv, PRIV_PROC_SESSION);
942634e26ecSCasper H.S. Dik (void) priv_delset(priv, PRIV_FILE_LINK_ANY);
943a98aba88Sgww if ((priv_addset(priv, PRIV_PROC_AUDIT) != 0) ||
944a98aba88Sgww (setppriv(PRIV_SET, PRIV_PERMITTED, priv) != 0)) {
945a98aba88Sgww syslog(LOG_AUTH | LOG_ALERT,
946a98aba88Sgww "su audit_logout: could not reduce privs: %m");
947a98aba88Sgww }
948a98aba88Sgww closefrom(0);
949a98aba88Sgww priv_freeset(priv);
95006ccc4b8SMarek Pospisil
95106ccc4b8SMarek Pospisil for (;;) {
95206ccc4b8SMarek Pospisil if (pid != waitpid(pid, &status, WUNTRACED)) {
95306ccc4b8SMarek Pospisil if (errno == ECHILD) {
95406ccc4b8SMarek Pospisil /*
95506ccc4b8SMarek Pospisil * No existing child with the given pid. Lets
95606ccc4b8SMarek Pospisil * audit the logout.
95706ccc4b8SMarek Pospisil */
95806ccc4b8SMarek Pospisil break;
95906ccc4b8SMarek Pospisil }
96006ccc4b8SMarek Pospisil continue;
96106ccc4b8SMarek Pospisil }
96206ccc4b8SMarek Pospisil
96306ccc4b8SMarek Pospisil if (WIFEXITED(status) || WIFSIGNALED(status)) {
96406ccc4b8SMarek Pospisil /*
96506ccc4b8SMarek Pospisil * The child shell exited or was terminated by
96606ccc4b8SMarek Pospisil * a signal. Lets audit logout.
96706ccc4b8SMarek Pospisil */
96806ccc4b8SMarek Pospisil break;
96906ccc4b8SMarek Pospisil } else if (WIFSTOPPED(status)) {
97006ccc4b8SMarek Pospisil pid_t pgid;
97106ccc4b8SMarek Pospisil int fd;
97206ccc4b8SMarek Pospisil void (*sg_handler)();
97306ccc4b8SMarek Pospisil /*
97406ccc4b8SMarek Pospisil * The child shell has been stopped/suspended.
97506ccc4b8SMarek Pospisil * We need to suspend here as well and pass down
97606ccc4b8SMarek Pospisil * the control to the parent process.
97706ccc4b8SMarek Pospisil */
97806ccc4b8SMarek Pospisil sg_handler = signal(WSTOPSIG(status), SIG_DFL);
97906ccc4b8SMarek Pospisil (void) sigsend(P_PGID, getpgrp(), WSTOPSIG(status));
98006ccc4b8SMarek Pospisil /*
98106ccc4b8SMarek Pospisil * We stop here. When resumed, mark the child
98206ccc4b8SMarek Pospisil * shell group as foreground process group
98306ccc4b8SMarek Pospisil * which gives the child shell a control over
98406ccc4b8SMarek Pospisil * the controlling terminal.
98506ccc4b8SMarek Pospisil */
98606ccc4b8SMarek Pospisil (void) signal(WSTOPSIG(status), sg_handler);
98706ccc4b8SMarek Pospisil
98806ccc4b8SMarek Pospisil pgid = getpgid(pid);
98906ccc4b8SMarek Pospisil if ((fd = open("/dev/tty", O_RDWR)) != -1) {
99006ccc4b8SMarek Pospisil /*
99106ccc4b8SMarek Pospisil * Pass down the control over the controlling
99206ccc4b8SMarek Pospisil * terminal iff we are in a foreground process
99306ccc4b8SMarek Pospisil * group. Otherwise, we are in a background
99406ccc4b8SMarek Pospisil * process group and the kernel will send
99506ccc4b8SMarek Pospisil * SIGTTOU signal to stop us (by default).
99606ccc4b8SMarek Pospisil */
99706ccc4b8SMarek Pospisil if (tcgetpgrp(fd) == getpgrp()) {
99806ccc4b8SMarek Pospisil (void) tcsetpgrp(fd, pgid);
99906ccc4b8SMarek Pospisil }
100006ccc4b8SMarek Pospisil (void) close(fd);
100106ccc4b8SMarek Pospisil }
100206ccc4b8SMarek Pospisil /* Wake up the child shell */
100306ccc4b8SMarek Pospisil (void) sigsend(P_PGID, pgid, SIGCONT);
100406ccc4b8SMarek Pospisil }
100506ccc4b8SMarek Pospisil }
1006a98aba88Sgww
1007a98aba88Sgww (void) adt_put_event(event, ADT_SUCCESS, ADT_SUCCESS);
1008a98aba88Sgww adt_free_event(event);
1009a98aba88Sgww (void) adt_end_session(ah);
1010a98aba88Sgww exit(WEXITSTATUS(status));
10115435d801Sgww }
10125435d801Sgww
10135435d801Sgww
10145435d801Sgww /*
10155435d801Sgww * audit_failure - audit failed su
10165435d801Sgww *
10175435d801Sgww * Entry New audit context not set.
10185435d801Sgww * pw_change == PW_FALSE, if no password change requested.
10195435d801Sgww * PW_FAILED, if failed password change audit
10205435d801Sgww * required.
102109295472Sgww * pwd = NULL, or password entry to use.
102209295472Sgww * user = username entered. Add to record if pwd == NULL.
10235435d801Sgww * pamerr = PAM error code; reason for failure.
10245435d801Sgww */
10255435d801Sgww
10265435d801Sgww static void
audit_failure(int pw_change,struct passwd * pwd,char * user,int pamerr,boolean_t isrole)1027*9e678d63SAndy Fiddaman audit_failure(int pw_change, struct passwd *pwd, char *user, int pamerr,
1028*9e678d63SAndy Fiddaman boolean_t isrole)
10295435d801Sgww {
10305435d801Sgww adt_session_data_t *ah; /* audit session handle */
10315435d801Sgww adt_event_data_t *event; /* event to generate */
103209295472Sgww au_event_t event_id = ADT_su;
10335435d801Sgww
10345435d801Sgww if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
10355435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
10365435d801Sgww "adt_start_session(ADT_su, ADT_FAILURE): %m");
10375435d801Sgww return;
10385435d801Sgww }
103909295472Sgww
10405435d801Sgww if (pwd != NULL) {
10415435d801Sgww /* target user authenticated, merge audit state */
10425435d801Sgww if (adt_set_user(ah, pwd->pw_uid, pwd->pw_gid, pwd->pw_uid,
10435435d801Sgww pwd->pw_gid, NULL, ADT_UPDATE) != 0) {
10445435d801Sgww syslog(LOG_AUTH | LOG_ERR,
10455435d801Sgww "adt_set_user(ADT_su, ADT_FAILURE): %m");
10465435d801Sgww }
1047*9e678d63SAndy Fiddaman if (isrole)
104809295472Sgww event_id = ADT_role_login;
10495435d801Sgww }
105009295472Sgww if ((event = adt_alloc_event(ah, event_id)) == NULL) {
10515435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
10525435d801Sgww "adt_alloc_event(ADT_su, ADT_FAILURE): %m");
10535435d801Sgww return;
105409295472Sgww }
105509295472Sgww /*
105609295472Sgww * can't tell if user not found is a role, so always use su
105709295472Sgww * If we do pass in pwd when the JNI is fixed, then can
105809295472Sgww * distinguish and set name in both su and role_login
105909295472Sgww */
106009295472Sgww if (pwd == NULL) {
106109295472Sgww /*
106209295472Sgww * this should be "fail_user" rather than "message"
106309295472Sgww * see adt_xml. The JNI breaks, so for now we leave
106409295472Sgww * this alone.
106509295472Sgww */
106609295472Sgww event->adt_su.message = user;
106709295472Sgww }
106809295472Sgww if (adt_put_event(event, ADT_FAILURE,
10695435d801Sgww ADT_FAIL_PAM + pamerr) != 0) {
10705435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
10715435d801Sgww "adt_put_event(ADT_su(ADT_FAIL, %s): %m",
10725435d801Sgww pam_strerror(pamh, pamerr));
10735435d801Sgww }
10745435d801Sgww if (pw_change != PW_FALSE) {
10755435d801Sgww /* Also audit password change failed */
10765435d801Sgww adt_free_event(event);
10775435d801Sgww if ((event = adt_alloc_event(ah, ADT_passwd)) == NULL) {
10785435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
1079a98aba88Sgww "su: adt_alloc_event(ADT_passwd): %m");
10805435d801Sgww } else if (adt_put_event(event, ADT_FAILURE,
10815435d801Sgww ADT_FAIL_PAM + pamerr) != 0) {
10825435d801Sgww syslog(LOG_AUTH | LOG_ALERT,
1083a98aba88Sgww "su: adt_put_event(ADT_passwd, ADT_FAILURE): %m");
10845435d801Sgww }
10855435d801Sgww }
10865435d801Sgww adt_free_event(event);
108709295472Sgww (void) adt_end_session(ah);
10885435d801Sgww }
10895435d801Sgww
10907c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
10917c478bd9Sstevel@tonic-gate /*
10927c478bd9Sstevel@tonic-gate * su_conv():
10937c478bd9Sstevel@tonic-gate * This is the conv (conversation) function called from
10947c478bd9Sstevel@tonic-gate * a PAM authentication module to print error messages
10957c478bd9Sstevel@tonic-gate * or garner information from the user.
10967c478bd9Sstevel@tonic-gate */
10977c478bd9Sstevel@tonic-gate /*ARGSUSED*/
10987c478bd9Sstevel@tonic-gate static int
su_conv(int num_msg,struct pam_message ** msg,struct pam_response ** response,void * appdata_ptr)10997c478bd9Sstevel@tonic-gate su_conv(int num_msg, struct pam_message **msg, struct pam_response **response,
11007c478bd9Sstevel@tonic-gate void *appdata_ptr)
11017c478bd9Sstevel@tonic-gate {
11027c478bd9Sstevel@tonic-gate struct pam_message *m;
11037c478bd9Sstevel@tonic-gate struct pam_response *r;
11047c478bd9Sstevel@tonic-gate char *temp;
11057c478bd9Sstevel@tonic-gate int k;
11067c478bd9Sstevel@tonic-gate char respbuf[PAM_MAX_RESP_SIZE];
11077c478bd9Sstevel@tonic-gate
11087c478bd9Sstevel@tonic-gate if (num_msg <= 0)
11097c478bd9Sstevel@tonic-gate return (PAM_CONV_ERR);
11107c478bd9Sstevel@tonic-gate
11117c478bd9Sstevel@tonic-gate *response = (struct pam_response *)calloc(num_msg,
11127c478bd9Sstevel@tonic-gate sizeof (struct pam_response));
11137c478bd9Sstevel@tonic-gate if (*response == NULL)
11147c478bd9Sstevel@tonic-gate return (PAM_BUF_ERR);
11157c478bd9Sstevel@tonic-gate
11167c478bd9Sstevel@tonic-gate k = num_msg;
11177c478bd9Sstevel@tonic-gate m = *msg;
11187c478bd9Sstevel@tonic-gate r = *response;
11197c478bd9Sstevel@tonic-gate while (k--) {
11207c478bd9Sstevel@tonic-gate
11217c478bd9Sstevel@tonic-gate switch (m->msg_style) {
11227c478bd9Sstevel@tonic-gate
11237c478bd9Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF:
11247c478bd9Sstevel@tonic-gate errno = 0;
11257c478bd9Sstevel@tonic-gate temp = getpassphrase(m->msg);
11267c478bd9Sstevel@tonic-gate if (errno == EINTR)
11277c478bd9Sstevel@tonic-gate return (PAM_CONV_ERR);
11287c478bd9Sstevel@tonic-gate if (temp != NULL) {
11297c478bd9Sstevel@tonic-gate r->resp = strdup(temp);
11307c478bd9Sstevel@tonic-gate if (r->resp == NULL) {
11317c478bd9Sstevel@tonic-gate freeresponse(num_msg, response);
11327c478bd9Sstevel@tonic-gate return (PAM_BUF_ERR);
11337c478bd9Sstevel@tonic-gate }
11347c478bd9Sstevel@tonic-gate }
11357c478bd9Sstevel@tonic-gate break;
11367c478bd9Sstevel@tonic-gate
11377c478bd9Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON:
11387c478bd9Sstevel@tonic-gate if (m->msg != NULL) {
11397c478bd9Sstevel@tonic-gate (void) fputs(m->msg, stdout);
11407c478bd9Sstevel@tonic-gate }
11417c478bd9Sstevel@tonic-gate
11427c478bd9Sstevel@tonic-gate (void) fgets(respbuf, sizeof (respbuf), stdin);
11437c478bd9Sstevel@tonic-gate temp = strchr(respbuf, '\n');
11447c478bd9Sstevel@tonic-gate if (temp != NULL)
11457c478bd9Sstevel@tonic-gate *temp = '\0';
11467c478bd9Sstevel@tonic-gate
11477c478bd9Sstevel@tonic-gate r->resp = strdup(respbuf);
11487c478bd9Sstevel@tonic-gate if (r->resp == NULL) {
11497c478bd9Sstevel@tonic-gate freeresponse(num_msg, response);
11507c478bd9Sstevel@tonic-gate return (PAM_BUF_ERR);
11517c478bd9Sstevel@tonic-gate }
11527c478bd9Sstevel@tonic-gate break;
11537c478bd9Sstevel@tonic-gate
11547c478bd9Sstevel@tonic-gate case PAM_ERROR_MSG:
11557c478bd9Sstevel@tonic-gate if (m->msg != NULL) {
11567c478bd9Sstevel@tonic-gate (void) fputs(m->msg, stderr);
11577c478bd9Sstevel@tonic-gate (void) fputs("\n", stderr);
11587c478bd9Sstevel@tonic-gate }
11597c478bd9Sstevel@tonic-gate break;
11607c478bd9Sstevel@tonic-gate
11617c478bd9Sstevel@tonic-gate case PAM_TEXT_INFO:
11627c478bd9Sstevel@tonic-gate if (m->msg != NULL) {
11637c478bd9Sstevel@tonic-gate (void) fputs(m->msg, stdout);
11647c478bd9Sstevel@tonic-gate (void) fputs("\n", stdout);
11657c478bd9Sstevel@tonic-gate }
11667c478bd9Sstevel@tonic-gate break;
11677c478bd9Sstevel@tonic-gate
11687c478bd9Sstevel@tonic-gate default:
11697c478bd9Sstevel@tonic-gate break;
11707c478bd9Sstevel@tonic-gate }
11717c478bd9Sstevel@tonic-gate m++;
11727c478bd9Sstevel@tonic-gate r++;
11737c478bd9Sstevel@tonic-gate }
11747c478bd9Sstevel@tonic-gate return (PAM_SUCCESS);
11757c478bd9Sstevel@tonic-gate }
11767c478bd9Sstevel@tonic-gate
11777c478bd9Sstevel@tonic-gate /*
11787c478bd9Sstevel@tonic-gate * emb_su_conv():
11797c478bd9Sstevel@tonic-gate * This is the conv (conversation) function called from
11807c478bd9Sstevel@tonic-gate * a PAM authentication module to print error messages
11817c478bd9Sstevel@tonic-gate * or garner information from the user.
11827c478bd9Sstevel@tonic-gate * This version is used for embedded_su.
11837c478bd9Sstevel@tonic-gate */
11847c478bd9Sstevel@tonic-gate /*ARGSUSED*/
11857c478bd9Sstevel@tonic-gate static int
emb_su_conv(int num_msg,struct pam_message ** msg,struct pam_response ** response,void * appdata_ptr)11867c478bd9Sstevel@tonic-gate emb_su_conv(int num_msg, struct pam_message **msg,
11877c478bd9Sstevel@tonic-gate struct pam_response **response, void *appdata_ptr)
11887c478bd9Sstevel@tonic-gate {
11897c478bd9Sstevel@tonic-gate struct pam_message *m;
11907c478bd9Sstevel@tonic-gate struct pam_response *r;
11917c478bd9Sstevel@tonic-gate char *temp;
11927c478bd9Sstevel@tonic-gate int k;
11937c478bd9Sstevel@tonic-gate char respbuf[PAM_MAX_RESP_SIZE];
11947c478bd9Sstevel@tonic-gate
11957c478bd9Sstevel@tonic-gate if (num_msg <= 0)
11967c478bd9Sstevel@tonic-gate return (PAM_CONV_ERR);
11977c478bd9Sstevel@tonic-gate
11987c478bd9Sstevel@tonic-gate *response = (struct pam_response *)calloc(num_msg,
11997c478bd9Sstevel@tonic-gate sizeof (struct pam_response));
12007c478bd9Sstevel@tonic-gate if (*response == NULL)
12017c478bd9Sstevel@tonic-gate return (PAM_BUF_ERR);
12027c478bd9Sstevel@tonic-gate
12037c478bd9Sstevel@tonic-gate /* First, send the prompts */
12047c478bd9Sstevel@tonic-gate (void) printf("CONV %d\n", num_msg);
12057c478bd9Sstevel@tonic-gate k = num_msg;
12067c478bd9Sstevel@tonic-gate m = *msg;
12077c478bd9Sstevel@tonic-gate while (k--) {
12087c478bd9Sstevel@tonic-gate switch (m->msg_style) {
12097c478bd9Sstevel@tonic-gate
12107c478bd9Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF:
12117c478bd9Sstevel@tonic-gate (void) puts("PAM_PROMPT_ECHO_OFF");
12127c478bd9Sstevel@tonic-gate goto msg_common;
12137c478bd9Sstevel@tonic-gate
12147c478bd9Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON:
12157c478bd9Sstevel@tonic-gate (void) puts("PAM_PROMPT_ECHO_ON");
12167c478bd9Sstevel@tonic-gate goto msg_common;
12177c478bd9Sstevel@tonic-gate
12187c478bd9Sstevel@tonic-gate case PAM_ERROR_MSG:
12197c478bd9Sstevel@tonic-gate (void) puts("PAM_ERROR_MSG");
12207c478bd9Sstevel@tonic-gate goto msg_common;
12217c478bd9Sstevel@tonic-gate
12227c478bd9Sstevel@tonic-gate case PAM_TEXT_INFO:
12237c478bd9Sstevel@tonic-gate (void) puts("PAM_TEXT_INFO");
12247c478bd9Sstevel@tonic-gate /* fall through to msg_common */
12257c478bd9Sstevel@tonic-gate msg_common:
12267c478bd9Sstevel@tonic-gate if (m->msg == NULL)
12277c478bd9Sstevel@tonic-gate quotemsg(NULL);
12287c478bd9Sstevel@tonic-gate else
12297c478bd9Sstevel@tonic-gate quotemsg("%s", m->msg);
12307c478bd9Sstevel@tonic-gate break;
12317c478bd9Sstevel@tonic-gate
12327c478bd9Sstevel@tonic-gate default:
12337c478bd9Sstevel@tonic-gate break;
12347c478bd9Sstevel@tonic-gate }
12357c478bd9Sstevel@tonic-gate m++;
12367c478bd9Sstevel@tonic-gate }
12377c478bd9Sstevel@tonic-gate
12387c478bd9Sstevel@tonic-gate /* Next, collect the responses */
12397c478bd9Sstevel@tonic-gate k = num_msg;
12407c478bd9Sstevel@tonic-gate m = *msg;
12417c478bd9Sstevel@tonic-gate r = *response;
12427c478bd9Sstevel@tonic-gate while (k--) {
12437c478bd9Sstevel@tonic-gate
12447c478bd9Sstevel@tonic-gate switch (m->msg_style) {
12457c478bd9Sstevel@tonic-gate
12467c478bd9Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF:
12477c478bd9Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON:
12487c478bd9Sstevel@tonic-gate (void) fgets(respbuf, sizeof (respbuf), stdin);
12497c478bd9Sstevel@tonic-gate
12507c478bd9Sstevel@tonic-gate temp = strchr(respbuf, '\n');
12517c478bd9Sstevel@tonic-gate if (temp != NULL)
12527c478bd9Sstevel@tonic-gate *temp = '\0';
12537c478bd9Sstevel@tonic-gate
12547c478bd9Sstevel@tonic-gate r->resp = strdup(respbuf);
12557c478bd9Sstevel@tonic-gate if (r->resp == NULL) {
12567c478bd9Sstevel@tonic-gate freeresponse(num_msg, response);
12577c478bd9Sstevel@tonic-gate return (PAM_BUF_ERR);
12587c478bd9Sstevel@tonic-gate }
12597c478bd9Sstevel@tonic-gate
12607c478bd9Sstevel@tonic-gate break;
12617c478bd9Sstevel@tonic-gate
12627c478bd9Sstevel@tonic-gate case PAM_ERROR_MSG:
12637c478bd9Sstevel@tonic-gate case PAM_TEXT_INFO:
12647c478bd9Sstevel@tonic-gate break;
12657c478bd9Sstevel@tonic-gate
12667c478bd9Sstevel@tonic-gate default:
12677c478bd9Sstevel@tonic-gate break;
12687c478bd9Sstevel@tonic-gate }
12697c478bd9Sstevel@tonic-gate m++;
12707c478bd9Sstevel@tonic-gate r++;
12717c478bd9Sstevel@tonic-gate }
12727c478bd9Sstevel@tonic-gate return (PAM_SUCCESS);
12737c478bd9Sstevel@tonic-gate }
12747c478bd9Sstevel@tonic-gate
12757c478bd9Sstevel@tonic-gate static void
freeresponse(int num_msg,struct pam_response ** response)12767c478bd9Sstevel@tonic-gate freeresponse(int num_msg, struct pam_response **response)
12777c478bd9Sstevel@tonic-gate {
12787c478bd9Sstevel@tonic-gate struct pam_response *r;
12797c478bd9Sstevel@tonic-gate int i;
12807c478bd9Sstevel@tonic-gate
12817c478bd9Sstevel@tonic-gate /* free responses */
12827c478bd9Sstevel@tonic-gate r = *response;
12837c478bd9Sstevel@tonic-gate for (i = 0; i < num_msg; i++, r++) {
12847c478bd9Sstevel@tonic-gate if (r->resp != NULL) {
12857c478bd9Sstevel@tonic-gate /* Zap it in case it's a password */
12867c478bd9Sstevel@tonic-gate (void) memset(r->resp, '\0', strlen(r->resp));
12877c478bd9Sstevel@tonic-gate free(r->resp);
12887c478bd9Sstevel@tonic-gate }
12897c478bd9Sstevel@tonic-gate }
12907c478bd9Sstevel@tonic-gate free(*response);
12917c478bd9Sstevel@tonic-gate *response = NULL;
12927c478bd9Sstevel@tonic-gate }
12937c478bd9Sstevel@tonic-gate
12947c478bd9Sstevel@tonic-gate /*
12957c478bd9Sstevel@tonic-gate * Print a message, applying quoting for lines starting with '.'.
12967c478bd9Sstevel@tonic-gate *
12977c478bd9Sstevel@tonic-gate * I18n note: \n is "safe" in all locales, and all locales use
12987c478bd9Sstevel@tonic-gate * a high-bit-set character to start multibyte sequences, so
12997c478bd9Sstevel@tonic-gate * scanning for a \n followed by a '.' is safe.
13007c478bd9Sstevel@tonic-gate */
13017c478bd9Sstevel@tonic-gate static void
quotemsg(char * fmt,...)13027c478bd9Sstevel@tonic-gate quotemsg(char *fmt, ...)
13037c478bd9Sstevel@tonic-gate {
13047c478bd9Sstevel@tonic-gate if (fmt != NULL) {
13057c478bd9Sstevel@tonic-gate char *msg;
13067c478bd9Sstevel@tonic-gate char *p;
13077c478bd9Sstevel@tonic-gate boolean_t bol;
13087c478bd9Sstevel@tonic-gate va_list v;
13097c478bd9Sstevel@tonic-gate
13107c478bd9Sstevel@tonic-gate va_start(v, fmt);
13117c478bd9Sstevel@tonic-gate msg = alloc_vsprintf(fmt, v);
13127c478bd9Sstevel@tonic-gate va_end(v);
13137c478bd9Sstevel@tonic-gate
13147c478bd9Sstevel@tonic-gate bol = B_TRUE;
13157c478bd9Sstevel@tonic-gate for (p = msg; *p != '\0'; p++) {
13167c478bd9Sstevel@tonic-gate if (bol) {
13177c478bd9Sstevel@tonic-gate if (*p == '.')
13187c478bd9Sstevel@tonic-gate (void) putchar('.');
13197c478bd9Sstevel@tonic-gate bol = B_FALSE;
13207c478bd9Sstevel@tonic-gate }
13217c478bd9Sstevel@tonic-gate (void) putchar(*p);
13227c478bd9Sstevel@tonic-gate if (*p == '\n')
13237c478bd9Sstevel@tonic-gate bol = B_TRUE;
13247c478bd9Sstevel@tonic-gate }
13257c478bd9Sstevel@tonic-gate (void) putchar('\n');
13267c478bd9Sstevel@tonic-gate free(msg);
13277c478bd9Sstevel@tonic-gate }
13287c478bd9Sstevel@tonic-gate (void) putchar('.');
13297c478bd9Sstevel@tonic-gate (void) putchar('\n');
13307c478bd9Sstevel@tonic-gate }
13317c478bd9Sstevel@tonic-gate
13327c478bd9Sstevel@tonic-gate /*
13337c478bd9Sstevel@tonic-gate * validate - Check that the account is valid for switching to.
13347c478bd9Sstevel@tonic-gate */
13357c478bd9Sstevel@tonic-gate static void
validate(char * usernam,int * pw_change,boolean_t isrole)1336*9e678d63SAndy Fiddaman validate(char *usernam, int *pw_change, boolean_t isrole)
13377c478bd9Sstevel@tonic-gate {
13385435d801Sgww int error;
13395435d801Sgww int tries;
13405435d801Sgww
134157c40785SJoep Vesseur if ((error = pam_acct_mgmt(pamh, pam_flags)) != PAM_SUCCESS) {
13427c478bd9Sstevel@tonic-gate if (Sulog != NULL)
13437c478bd9Sstevel@tonic-gate log(Sulog, pwd.pw_name, 0); /* log entry */
13447c478bd9Sstevel@tonic-gate if (error == PAM_NEW_AUTHTOK_REQD) {
13455435d801Sgww tries = 0;
13467c478bd9Sstevel@tonic-gate message(ERR, gettext("Password for user "
13475435d801Sgww "'%s' has expired"), pwd.pw_name);
1348f00e6aa6Sdarrenm while ((error = pam_chauthtok(pamh,
1349f00e6aa6Sdarrenm PAM_CHANGE_EXPIRED_AUTHTOK)) != PAM_SUCCESS) {
13505435d801Sgww if ((error == PAM_AUTHTOK_ERR ||
13515435d801Sgww error == PAM_TRY_AGAIN) &&
13525435d801Sgww (tries++ < DEF_ATTEMPTS)) {
13535435d801Sgww continue;
13545435d801Sgww }
13555435d801Sgww message(ERR, gettext("Sorry"));
1356*9e678d63SAndy Fiddaman audit_failure(PW_FAILED, &pwd, NULL, error,
1357*9e678d63SAndy Fiddaman isrole);
13585435d801Sgww if (dosyslog)
13595435d801Sgww syslog(LOG_CRIT,
13605435d801Sgww "'su %s' failed for %s on %s",
13615435d801Sgww pwd.pw_name, usernam, ttyn);
13625435d801Sgww closelog();
13635435d801Sgww exit(1);
13645435d801Sgww }
13655435d801Sgww *pw_change = PW_TRUE;
13665435d801Sgww return;
13677c478bd9Sstevel@tonic-gate } else {
13687c478bd9Sstevel@tonic-gate message(ERR, gettext("Sorry"));
1369*9e678d63SAndy Fiddaman audit_failure(PW_FALSE, &pwd, NULL, error, isrole);
13707c478bd9Sstevel@tonic-gate if (dosyslog)
137186ecf0b4SJan Kryl syslog(LOG_CRIT, "'su %s' failed for %s on %s",
137286ecf0b4SJan Kryl pwd.pw_name, usernam, ttyn);
13737c478bd9Sstevel@tonic-gate closelog();
13747c478bd9Sstevel@tonic-gate exit(3);
13757c478bd9Sstevel@tonic-gate }
13767c478bd9Sstevel@tonic-gate }
13777c478bd9Sstevel@tonic-gate }
13787c478bd9Sstevel@tonic-gate
13797c478bd9Sstevel@tonic-gate static char *illegal[] = {
13807c478bd9Sstevel@tonic-gate "SHELL=",
13817c478bd9Sstevel@tonic-gate "HOME=",
13827c478bd9Sstevel@tonic-gate "LOGNAME=",
13837c478bd9Sstevel@tonic-gate #ifndef NO_MAIL
13847c478bd9Sstevel@tonic-gate "MAIL=",
13857c478bd9Sstevel@tonic-gate #endif
13867c478bd9Sstevel@tonic-gate "CDPATH=",
13877c478bd9Sstevel@tonic-gate "IFS=",
13887c478bd9Sstevel@tonic-gate "PATH=",
13897c478bd9Sstevel@tonic-gate "TZ=",
13907c478bd9Sstevel@tonic-gate "HZ=",
13917c478bd9Sstevel@tonic-gate "TERM=",
13927c478bd9Sstevel@tonic-gate 0
13937c478bd9Sstevel@tonic-gate };
13947c478bd9Sstevel@tonic-gate
13957c478bd9Sstevel@tonic-gate /*
13967c478bd9Sstevel@tonic-gate * legalenvvar - can PAM modules insert this environmental variable?
13977c478bd9Sstevel@tonic-gate */
13987c478bd9Sstevel@tonic-gate
13997c478bd9Sstevel@tonic-gate static int
legalenvvar(char * s)14007c478bd9Sstevel@tonic-gate legalenvvar(char *s)
14017c478bd9Sstevel@tonic-gate {
14027c478bd9Sstevel@tonic-gate register char **p;
14037c478bd9Sstevel@tonic-gate
14047c478bd9Sstevel@tonic-gate for (p = illegal; *p; p++)
14057c478bd9Sstevel@tonic-gate if (strncmp(s, *p, strlen(*p)) == 0)
14067c478bd9Sstevel@tonic-gate return (0);
14077c478bd9Sstevel@tonic-gate
14087c478bd9Sstevel@tonic-gate if (s[0] == 'L' && s[1] == 'D' && s[2] == '_')
14097c478bd9Sstevel@tonic-gate return (0);
14107c478bd9Sstevel@tonic-gate
14117c478bd9Sstevel@tonic-gate return (1);
14127c478bd9Sstevel@tonic-gate }
14137c478bd9Sstevel@tonic-gate
14147c478bd9Sstevel@tonic-gate /*
14157c478bd9Sstevel@tonic-gate * The embedded_su protocol allows the client application to supply
14167c478bd9Sstevel@tonic-gate * an initialization block terminated by a line with just a "." on it.
14177c478bd9Sstevel@tonic-gate *
14187c478bd9Sstevel@tonic-gate * This initialization block is currently unused, reserved for future
14197c478bd9Sstevel@tonic-gate * expansion. Ignore it. This is made very slightly more complex by
14207c478bd9Sstevel@tonic-gate * the desire to cleanly ignore input lines of any length, while still
14217c478bd9Sstevel@tonic-gate * correctly detecting a line with just a "." on it.
14227c478bd9Sstevel@tonic-gate *
14237c478bd9Sstevel@tonic-gate * I18n note: It appears that none of the Solaris-supported locales
14247c478bd9Sstevel@tonic-gate * use 0x0a for any purpose other than newline, so looking for '\n'
14257c478bd9Sstevel@tonic-gate * seems safe.
14267c478bd9Sstevel@tonic-gate * All locales use high-bit-set leadin characters for their multi-byte
14277c478bd9Sstevel@tonic-gate * sequences, so a line consisting solely of ".\n" is what it appears
14287c478bd9Sstevel@tonic-gate * to be.
14297c478bd9Sstevel@tonic-gate */
14307c478bd9Sstevel@tonic-gate static void
readinitblock(void)14317c478bd9Sstevel@tonic-gate readinitblock(void)
14327c478bd9Sstevel@tonic-gate {
14337c478bd9Sstevel@tonic-gate char buf[100];
14347c478bd9Sstevel@tonic-gate boolean_t bol;
14357c478bd9Sstevel@tonic-gate
14367c478bd9Sstevel@tonic-gate bol = B_TRUE;
14377c478bd9Sstevel@tonic-gate for (;;) {
14387c478bd9Sstevel@tonic-gate if (fgets(buf, sizeof (buf), stdin) == NULL)
14397c478bd9Sstevel@tonic-gate return;
14407c478bd9Sstevel@tonic-gate if (bol && strcmp(buf, ".\n") == 0)
14417c478bd9Sstevel@tonic-gate return;
14427c478bd9Sstevel@tonic-gate bol = (strchr(buf, '\n') != NULL);
14437c478bd9Sstevel@tonic-gate }
14447c478bd9Sstevel@tonic-gate }
14455435d801Sgww #else /* !DYNAMIC_SU */
14465435d801Sgww static void
update_audit(struct passwd * pwd)14475435d801Sgww update_audit(struct passwd *pwd)
14485435d801Sgww {
14495435d801Sgww adt_session_data_t *ah; /* audit session handle */
14505435d801Sgww
14515435d801Sgww if (adt_start_session(&ah, NULL, ADT_USE_PROC_DATA) != 0) {
14525435d801Sgww message(ERR, gettext("Sorry"));
14535435d801Sgww if (dosyslog)
14545435d801Sgww syslog(LOG_CRIT, "'su %s' failed for %s "
14555435d801Sgww "cannot start audit session %m",
14565435d801Sgww pwd->pw_name, username);
14575435d801Sgww closelog();
14585435d801Sgww exit(2);
14595435d801Sgww }
14605435d801Sgww if (adt_set_user(ah, pwd->pw_uid, pwd->pw_gid, pwd->pw_uid,
14615435d801Sgww pwd->pw_gid, NULL, ADT_UPDATE) != 0) {
14625435d801Sgww if (dosyslog)
14635435d801Sgww syslog(LOG_CRIT, "'su %s' failed for %s "
14645435d801Sgww "cannot update audit session %m",
14655435d801Sgww pwd->pw_name, username);
14665435d801Sgww closelog();
14675435d801Sgww exit(2);
14685435d801Sgww }
14695435d801Sgww }
14707c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
14717c478bd9Sstevel@tonic-gate
14727c478bd9Sstevel@tonic-gate /*
14737c478bd9Sstevel@tonic-gate * Report an error, either a fatal one, a warning, or a usage message,
14747c478bd9Sstevel@tonic-gate * depending on the mode parameter.
14757c478bd9Sstevel@tonic-gate */
14767c478bd9Sstevel@tonic-gate /*ARGSUSED*/
14777c478bd9Sstevel@tonic-gate static void
message(enum messagemode mode,char * fmt,...)14787c478bd9Sstevel@tonic-gate message(enum messagemode mode, char *fmt, ...)
14797c478bd9Sstevel@tonic-gate {
14807c478bd9Sstevel@tonic-gate char *s;
14817c478bd9Sstevel@tonic-gate va_list v;
14827c478bd9Sstevel@tonic-gate
14837c478bd9Sstevel@tonic-gate va_start(v, fmt);
14847c478bd9Sstevel@tonic-gate s = alloc_vsprintf(fmt, v);
14857c478bd9Sstevel@tonic-gate va_end(v);
14867c478bd9Sstevel@tonic-gate
14877c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
14887c478bd9Sstevel@tonic-gate if (embedded) {
14897c478bd9Sstevel@tonic-gate if (mode == WARN) {
14907c478bd9Sstevel@tonic-gate (void) printf("CONV 1\n");
14917c478bd9Sstevel@tonic-gate (void) printf("PAM_ERROR_MSG\n");
14927c478bd9Sstevel@tonic-gate } else { /* ERR, USAGE */
14937c478bd9Sstevel@tonic-gate (void) printf("ERROR\n");
14947c478bd9Sstevel@tonic-gate }
14957c478bd9Sstevel@tonic-gate if (mode == USAGE) {
14967c478bd9Sstevel@tonic-gate quotemsg("%s", s);
14977c478bd9Sstevel@tonic-gate } else { /* ERR, WARN */
14987c478bd9Sstevel@tonic-gate quotemsg("%s: %s", myname, s);
14997c478bd9Sstevel@tonic-gate }
15007c478bd9Sstevel@tonic-gate } else {
15017c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
15027c478bd9Sstevel@tonic-gate if (mode == USAGE) {
15037c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s\n", s);
15047c478bd9Sstevel@tonic-gate } else { /* ERR, WARN */
15057c478bd9Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s\n", myname, s);
15067c478bd9Sstevel@tonic-gate }
15077c478bd9Sstevel@tonic-gate #ifdef DYNAMIC_SU
15087c478bd9Sstevel@tonic-gate }
15097c478bd9Sstevel@tonic-gate #endif /* DYNAMIC_SU */
15107c478bd9Sstevel@tonic-gate
15117c478bd9Sstevel@tonic-gate free(s);
15127c478bd9Sstevel@tonic-gate }
15137c478bd9Sstevel@tonic-gate
15147c478bd9Sstevel@tonic-gate /*
15157c478bd9Sstevel@tonic-gate * Return a pointer to the last path component of a.
15167c478bd9Sstevel@tonic-gate */
15177c478bd9Sstevel@tonic-gate static char *
tail(char * a)15187c478bd9Sstevel@tonic-gate tail(char *a)
15197c478bd9Sstevel@tonic-gate {
15207c478bd9Sstevel@tonic-gate char *p;
15217c478bd9Sstevel@tonic-gate
15227c478bd9Sstevel@tonic-gate p = strrchr(a, '/');
15237c478bd9Sstevel@tonic-gate if (p == NULL)
15247c478bd9Sstevel@tonic-gate p = a;
15257c478bd9Sstevel@tonic-gate else
15267c478bd9Sstevel@tonic-gate p++; /* step over the '/' */
15277c478bd9Sstevel@tonic-gate
15287c478bd9Sstevel@tonic-gate return (p);
15297c478bd9Sstevel@tonic-gate }
15307c478bd9Sstevel@tonic-gate
15317c478bd9Sstevel@tonic-gate static char *
alloc_vsprintf(const char * fmt,va_list ap1)15327c478bd9Sstevel@tonic-gate alloc_vsprintf(const char *fmt, va_list ap1)
15337c478bd9Sstevel@tonic-gate {
15347c478bd9Sstevel@tonic-gate va_list ap2;
15357c478bd9Sstevel@tonic-gate int n;
15367c478bd9Sstevel@tonic-gate char buf[1];
15377c478bd9Sstevel@tonic-gate char *s;
15387c478bd9Sstevel@tonic-gate
15397c478bd9Sstevel@tonic-gate /*
15407c478bd9Sstevel@tonic-gate * We need to scan the argument list twice. Save off a copy
15417c478bd9Sstevel@tonic-gate * of the argument list pointer(s) for the second pass. Note that
15427c478bd9Sstevel@tonic-gate * we are responsible for va_end'ing our copy.
15437c478bd9Sstevel@tonic-gate */
15447c478bd9Sstevel@tonic-gate va_copy(ap2, ap1);
15457c478bd9Sstevel@tonic-gate
15467c478bd9Sstevel@tonic-gate /*
15477c478bd9Sstevel@tonic-gate * vsnprintf into a dummy to get a length. One might
15487c478bd9Sstevel@tonic-gate * think that passing 0 as the length to snprintf would
15497c478bd9Sstevel@tonic-gate * do what we want, but it's defined not to.
15507c478bd9Sstevel@tonic-gate *
15517c478bd9Sstevel@tonic-gate * Perhaps we should sprintf into a 100 character buffer
15527c478bd9Sstevel@tonic-gate * or something like that, to avoid two calls to snprintf
15537c478bd9Sstevel@tonic-gate * in most cases.
15547c478bd9Sstevel@tonic-gate */
15557c478bd9Sstevel@tonic-gate n = vsnprintf(buf, sizeof (buf), fmt, ap2);
15567c478bd9Sstevel@tonic-gate va_end(ap2);
15577c478bd9Sstevel@tonic-gate
15587c478bd9Sstevel@tonic-gate /*
15597c478bd9Sstevel@tonic-gate * Allocate an appropriately-sized buffer.
15607c478bd9Sstevel@tonic-gate */
15617c478bd9Sstevel@tonic-gate s = malloc(n + 1);
15627c478bd9Sstevel@tonic-gate if (s == NULL) {
15637c478bd9Sstevel@tonic-gate perror("malloc");
15647c478bd9Sstevel@tonic-gate exit(4);
15657c478bd9Sstevel@tonic-gate }
15667c478bd9Sstevel@tonic-gate
15677c478bd9Sstevel@tonic-gate (void) vsnprintf(s, n+1, fmt, ap1);
15687c478bd9Sstevel@tonic-gate
15697c478bd9Sstevel@tonic-gate return (s);
15707c478bd9Sstevel@tonic-gate }
1571