1*0Sstevel@tonic-gate /* 2*0Sstevel@tonic-gate * CDDL HEADER START 3*0Sstevel@tonic-gate * 4*0Sstevel@tonic-gate * The contents of this file are subject to the terms of the 5*0Sstevel@tonic-gate * Common Development and Distribution License, Version 1.0 only 6*0Sstevel@tonic-gate * (the "License"). You may not use this file except in compliance 7*0Sstevel@tonic-gate * with the License. 8*0Sstevel@tonic-gate * 9*0Sstevel@tonic-gate * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10*0Sstevel@tonic-gate * or http://www.opensolaris.org/os/licensing. 11*0Sstevel@tonic-gate * See the License for the specific language governing permissions 12*0Sstevel@tonic-gate * and limitations under the License. 13*0Sstevel@tonic-gate * 14*0Sstevel@tonic-gate * When distributing Covered Code, include this CDDL HEADER in each 15*0Sstevel@tonic-gate * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16*0Sstevel@tonic-gate * If applicable, add the following below this CDDL HEADER, with the 17*0Sstevel@tonic-gate * fields enclosed by brackets "[]" replaced with your own identifying 18*0Sstevel@tonic-gate * information: Portions Copyright [yyyy] [name of copyright owner] 19*0Sstevel@tonic-gate * 20*0Sstevel@tonic-gate * CDDL HEADER END 21*0Sstevel@tonic-gate */ 22*0Sstevel@tonic-gate /* 23*0Sstevel@tonic-gate * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24*0Sstevel@tonic-gate * Use is subject to license terms. 25*0Sstevel@tonic-gate */ 26*0Sstevel@tonic-gate 27*0Sstevel@tonic-gate /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28*0Sstevel@tonic-gate /* All Rights Reserved */ 29*0Sstevel@tonic-gate 30*0Sstevel@tonic-gate /* Copyright (c) 1987, 1988 Microsoft Corporation */ 31*0Sstevel@tonic-gate /* All Rights Reserved */ 32*0Sstevel@tonic-gate 33*0Sstevel@tonic-gate #pragma ident "%Z%%M% %I% %E% SMI" 34*0Sstevel@tonic-gate 35*0Sstevel@tonic-gate /* 36*0Sstevel@tonic-gate * su [-] [name [arg ...]] change userid, `-' changes environment. 37*0Sstevel@tonic-gate * If SULOG is defined, all attempts to su to another user are 38*0Sstevel@tonic-gate * logged there. 39*0Sstevel@tonic-gate * If CONSOLE is defined, all successful attempts to su to uid 0 40*0Sstevel@tonic-gate * are also logged there. 41*0Sstevel@tonic-gate * 42*0Sstevel@tonic-gate * If su cannot create, open, or write entries into SULOG, 43*0Sstevel@tonic-gate * (or on the CONSOLE, if defined), the entry will not 44*0Sstevel@tonic-gate * be logged -- thus losing a record of the su's attempted 45*0Sstevel@tonic-gate * during this period. 46*0Sstevel@tonic-gate */ 47*0Sstevel@tonic-gate 48*0Sstevel@tonic-gate #include <stdio.h> 49*0Sstevel@tonic-gate #include <sys/types.h> 50*0Sstevel@tonic-gate #include <sys/stat.h> 51*0Sstevel@tonic-gate #include <sys/param.h> 52*0Sstevel@tonic-gate #include <unistd.h> 53*0Sstevel@tonic-gate #include <stdlib.h> 54*0Sstevel@tonic-gate #include <crypt.h> 55*0Sstevel@tonic-gate #include <pwd.h> 56*0Sstevel@tonic-gate #include <shadow.h> 57*0Sstevel@tonic-gate #include <time.h> 58*0Sstevel@tonic-gate #include <signal.h> 59*0Sstevel@tonic-gate #include <fcntl.h> 60*0Sstevel@tonic-gate #include <string.h> 61*0Sstevel@tonic-gate #include <locale.h> 62*0Sstevel@tonic-gate #include <syslog.h> 63*0Sstevel@tonic-gate #include <sys/utsname.h> 64*0Sstevel@tonic-gate #include <grp.h> 65*0Sstevel@tonic-gate #include <deflt.h> 66*0Sstevel@tonic-gate #include <limits.h> 67*0Sstevel@tonic-gate #include <errno.h> 68*0Sstevel@tonic-gate #include <stdarg.h> 69*0Sstevel@tonic-gate 70*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 71*0Sstevel@tonic-gate #include <security/pam_appl.h> 72*0Sstevel@tonic-gate #endif 73*0Sstevel@tonic-gate 74*0Sstevel@tonic-gate #define PATH "/usr/bin:" /* path for users other than root */ 75*0Sstevel@tonic-gate #define SUPATH "/usr/sbin:/usr/bin" /* path for root */ 76*0Sstevel@tonic-gate #define SUPRMT "PS1=# " /* primary prompt for root */ 77*0Sstevel@tonic-gate #define ELIM 128 78*0Sstevel@tonic-gate #define ROOT 0 79*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 80*0Sstevel@tonic-gate #define EMBEDDED_NAME "embedded_su" 81*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 82*0Sstevel@tonic-gate 83*0Sstevel@tonic-gate /* 84*0Sstevel@tonic-gate * Intervals to sleep after failed su 85*0Sstevel@tonic-gate */ 86*0Sstevel@tonic-gate #ifndef SLEEPTIME 87*0Sstevel@tonic-gate #define SLEEPTIME 4 88*0Sstevel@tonic-gate #endif 89*0Sstevel@tonic-gate 90*0Sstevel@tonic-gate #define DEFAULT_LOGIN "/etc/default/login" 91*0Sstevel@tonic-gate #define DEFFILE "/etc/default/su" 92*0Sstevel@tonic-gate 93*0Sstevel@tonic-gate 94*0Sstevel@tonic-gate char *Sulog, *Console; 95*0Sstevel@tonic-gate char *Path, *Supath; 96*0Sstevel@tonic-gate 97*0Sstevel@tonic-gate /* 98*0Sstevel@tonic-gate * Locale variables to be propagated to "su -" environment 99*0Sstevel@tonic-gate */ 100*0Sstevel@tonic-gate static char *initvar; 101*0Sstevel@tonic-gate static char *initenv[] = { 102*0Sstevel@tonic-gate "TZ", "LANG", "LC_CTYPE", 103*0Sstevel@tonic-gate "LC_NUMERIC", "LC_TIME", "LC_COLLATE", 104*0Sstevel@tonic-gate "LC_MONETARY", "LC_MESSAGES", "LC_ALL", 0}; 105*0Sstevel@tonic-gate static char mail[30] = { "MAIL=/var/mail/" }; 106*0Sstevel@tonic-gate 107*0Sstevel@tonic-gate static void envalt(void); 108*0Sstevel@tonic-gate static void log(char *where, char *towho, int how); 109*0Sstevel@tonic-gate static void to(int sig); 110*0Sstevel@tonic-gate 111*0Sstevel@tonic-gate enum messagemode { USAGE, ERR, WARN }; 112*0Sstevel@tonic-gate static void message(enum messagemode mode, char *fmt, ...); 113*0Sstevel@tonic-gate 114*0Sstevel@tonic-gate static char *alloc_vsprintf(const char *fmt, va_list ap1); 115*0Sstevel@tonic-gate static char *tail(char *a); 116*0Sstevel@tonic-gate 117*0Sstevel@tonic-gate /* 118*0Sstevel@tonic-gate * Obsolescent Audit hooks for su command 119*0Sstevel@tonic-gate */ 120*0Sstevel@tonic-gate extern void audit_su_bad_authentication(void); 121*0Sstevel@tonic-gate extern void audit_su_bad_username(void); 122*0Sstevel@tonic-gate extern void audit_su_init_info(char *, char *); 123*0Sstevel@tonic-gate extern void audit_su_reset_ai(void); 124*0Sstevel@tonic-gate extern void audit_su_success(void); 125*0Sstevel@tonic-gate extern void audit_su_unknown_failure(void); 126*0Sstevel@tonic-gate 127*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 128*0Sstevel@tonic-gate static void validate(char *usernam); 129*0Sstevel@tonic-gate static int legalenvvar(char *s); 130*0Sstevel@tonic-gate static int su_conv(int, struct pam_message **, struct pam_response **, void *); 131*0Sstevel@tonic-gate static int emb_su_conv(int, struct pam_message **, struct pam_response **, 132*0Sstevel@tonic-gate void *); 133*0Sstevel@tonic-gate static void freeresponse(int num_msg, struct pam_response **response); 134*0Sstevel@tonic-gate static struct pam_conv pam_conv = {su_conv, NULL}; 135*0Sstevel@tonic-gate static struct pam_conv emb_pam_conv = {emb_su_conv, NULL}; 136*0Sstevel@tonic-gate static pam_handle_t *pamh; /* Authentication handle */ 137*0Sstevel@tonic-gate static void quotemsg(char *fmt, ...); 138*0Sstevel@tonic-gate static void readinitblock(void); 139*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 140*0Sstevel@tonic-gate 141*0Sstevel@tonic-gate struct passwd pwd; 142*0Sstevel@tonic-gate char pwdbuf[1024]; /* buffer for getpwnam_r() */ 143*0Sstevel@tonic-gate char shell[] = "/usr/bin/sh"; /* default shell */ 144*0Sstevel@tonic-gate char safe_shell[] = "/sbin/sh"; /* "fallback" shell */ 145*0Sstevel@tonic-gate char su[PATH_MAX] = "su"; /* arg0 for exec of shprog */ 146*0Sstevel@tonic-gate char homedir[PATH_MAX] = "HOME="; 147*0Sstevel@tonic-gate char logname[20] = "LOGNAME="; 148*0Sstevel@tonic-gate char *suprmt = SUPRMT; 149*0Sstevel@tonic-gate char termtyp[PATH_MAX] = "TERM="; 150*0Sstevel@tonic-gate char *term; 151*0Sstevel@tonic-gate char shelltyp[PATH_MAX] = "SHELL="; 152*0Sstevel@tonic-gate char *hz; 153*0Sstevel@tonic-gate char tznam[PATH_MAX]; 154*0Sstevel@tonic-gate char hzname[10] = "HZ="; 155*0Sstevel@tonic-gate char path[PATH_MAX] = "PATH="; 156*0Sstevel@tonic-gate char supath[PATH_MAX] = "PATH="; 157*0Sstevel@tonic-gate char *envinit[ELIM]; 158*0Sstevel@tonic-gate extern char **environ; 159*0Sstevel@tonic-gate char *ttyn; 160*0Sstevel@tonic-gate char *username; /* the invoker */ 161*0Sstevel@tonic-gate static int dosyslog = 0; /* use syslog? */ 162*0Sstevel@tonic-gate char *myname; 163*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 164*0Sstevel@tonic-gate boolean_t embedded = B_FALSE; 165*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 166*0Sstevel@tonic-gate 167*0Sstevel@tonic-gate int 168*0Sstevel@tonic-gate main(int argc, char **argv) 169*0Sstevel@tonic-gate { 170*0Sstevel@tonic-gate #ifndef DYNAMIC_SU 171*0Sstevel@tonic-gate struct spwd sp; 172*0Sstevel@tonic-gate char spbuf[1024]; /* buffer for getspnam_r() */ 173*0Sstevel@tonic-gate char *password; 174*0Sstevel@tonic-gate #endif 175*0Sstevel@tonic-gate char *nptr; 176*0Sstevel@tonic-gate char *pshell; 177*0Sstevel@tonic-gate int eflag = 0; 178*0Sstevel@tonic-gate int envidx = 0; 179*0Sstevel@tonic-gate uid_t uid; 180*0Sstevel@tonic-gate gid_t gid; 181*0Sstevel@tonic-gate char *dir, *shprog, *name; 182*0Sstevel@tonic-gate char *ptr; 183*0Sstevel@tonic-gate char *prog = argv[0]; 184*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 185*0Sstevel@tonic-gate int sleeptime = SLEEPTIME; 186*0Sstevel@tonic-gate char **pam_env = 0; 187*0Sstevel@tonic-gate int flags = 0; 188*0Sstevel@tonic-gate int retcode; 189*0Sstevel@tonic-gate int idx = 0; 190*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 191*0Sstevel@tonic-gate 192*0Sstevel@tonic-gate (void) setlocale(LC_ALL, ""); 193*0Sstevel@tonic-gate #if !defined(TEXT_DOMAIN) /* Should be defined by cc -D */ 194*0Sstevel@tonic-gate #define TEXT_DOMAIN "SYS_TEST" /* Use this only if it wasn't */ 195*0Sstevel@tonic-gate #endif 196*0Sstevel@tonic-gate (void) textdomain(TEXT_DOMAIN); 197*0Sstevel@tonic-gate 198*0Sstevel@tonic-gate myname = tail(argv[0]); 199*0Sstevel@tonic-gate 200*0Sstevel@tonic-gate #if defined(DYNAMIC_SU) 201*0Sstevel@tonic-gate if (strcmp(myname, EMBEDDED_NAME) == 0) { 202*0Sstevel@tonic-gate embedded = B_TRUE; 203*0Sstevel@tonic-gate setbuf(stdin, NULL); 204*0Sstevel@tonic-gate setbuf(stdout, NULL); 205*0Sstevel@tonic-gate readinitblock(); 206*0Sstevel@tonic-gate } 207*0Sstevel@tonic-gate #endif /* defined(DYNAMIC_SU) */ 208*0Sstevel@tonic-gate 209*0Sstevel@tonic-gate if (argc > 1 && *argv[1] == '-') { 210*0Sstevel@tonic-gate /* Explicitly check for just `-' (no trailing chars) */ 211*0Sstevel@tonic-gate if (strlen(argv[1]) == 1) { 212*0Sstevel@tonic-gate eflag++; /* set eflag if `-' is specified */ 213*0Sstevel@tonic-gate argv++; 214*0Sstevel@tonic-gate argc--; 215*0Sstevel@tonic-gate } else { 216*0Sstevel@tonic-gate message(USAGE, 217*0Sstevel@tonic-gate gettext("Usage: %s [-] [ username [ arg ... ] ]"), 218*0Sstevel@tonic-gate prog); 219*0Sstevel@tonic-gate exit(1); 220*0Sstevel@tonic-gate } 221*0Sstevel@tonic-gate } 222*0Sstevel@tonic-gate 223*0Sstevel@tonic-gate /* 224*0Sstevel@tonic-gate * Determine specified userid, get their password file entry, 225*0Sstevel@tonic-gate * and set variables to values in password file entry fields. 226*0Sstevel@tonic-gate */ 227*0Sstevel@tonic-gate if (argc > 1) { 228*0Sstevel@tonic-gate /* 229*0Sstevel@tonic-gate * Usernames can't start with a `-', so we check for that to 230*0Sstevel@tonic-gate * catch bad usage (like "su - -c ls"). 231*0Sstevel@tonic-gate */ 232*0Sstevel@tonic-gate if (*argv[1] == '-') { 233*0Sstevel@tonic-gate message(USAGE, 234*0Sstevel@tonic-gate gettext("Usage: %s [-] [ username [ arg ... ] ]"), 235*0Sstevel@tonic-gate prog); 236*0Sstevel@tonic-gate exit(1); 237*0Sstevel@tonic-gate } else 238*0Sstevel@tonic-gate nptr = argv[1]; /* use valid command-line username */ 239*0Sstevel@tonic-gate } else 240*0Sstevel@tonic-gate nptr = "root"; /* use default "root" username */ 241*0Sstevel@tonic-gate 242*0Sstevel@tonic-gate if (defopen(DEFFILE) == 0) { 243*0Sstevel@tonic-gate 244*0Sstevel@tonic-gate if (Sulog = defread("SULOG=")) 245*0Sstevel@tonic-gate Sulog = strdup(Sulog); 246*0Sstevel@tonic-gate if (Console = defread("CONSOLE=")) 247*0Sstevel@tonic-gate Console = strdup(Console); 248*0Sstevel@tonic-gate if (Path = defread("PATH=")) 249*0Sstevel@tonic-gate Path = strdup(Path); 250*0Sstevel@tonic-gate if (Supath = defread("SUPATH=")) 251*0Sstevel@tonic-gate Supath = strdup(Supath); 252*0Sstevel@tonic-gate if ((ptr = defread("SYSLOG=")) != NULL) 253*0Sstevel@tonic-gate dosyslog = strcmp(ptr, "YES") == 0; 254*0Sstevel@tonic-gate 255*0Sstevel@tonic-gate (void) defopen(NULL); 256*0Sstevel@tonic-gate } 257*0Sstevel@tonic-gate (void) strlcat(path, (Path) ? Path : PATH, sizeof (path)); 258*0Sstevel@tonic-gate (void) strlcat(supath, (Supath) ? Supath : SUPATH, sizeof (supath)); 259*0Sstevel@tonic-gate 260*0Sstevel@tonic-gate if ((ttyn = ttyname(0)) == NULL) 261*0Sstevel@tonic-gate if ((ttyn = ttyname(1)) == NULL) 262*0Sstevel@tonic-gate if ((ttyn = ttyname(2)) == NULL) 263*0Sstevel@tonic-gate ttyn = "/dev/???"; 264*0Sstevel@tonic-gate if ((username = cuserid(NULL)) == NULL) 265*0Sstevel@tonic-gate username = "(null)"; 266*0Sstevel@tonic-gate 267*0Sstevel@tonic-gate /* 268*0Sstevel@tonic-gate * if Sulog defined, create SULOG, if it does not exist, with 269*0Sstevel@tonic-gate * mode read/write user. Change owner and group to root 270*0Sstevel@tonic-gate */ 271*0Sstevel@tonic-gate if (Sulog != NULL) { 272*0Sstevel@tonic-gate (void) close(open(Sulog, O_WRONLY | O_APPEND | O_CREAT, 273*0Sstevel@tonic-gate (S_IRUSR|S_IWUSR))); 274*0Sstevel@tonic-gate (void) chown(Sulog, (uid_t)ROOT, (gid_t)ROOT); 275*0Sstevel@tonic-gate } 276*0Sstevel@tonic-gate 277*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 278*0Sstevel@tonic-gate if (pam_start(embedded ? EMBEDDED_NAME : "su", nptr, 279*0Sstevel@tonic-gate embedded ? &emb_pam_conv : &pam_conv, &pamh) != PAM_SUCCESS) 280*0Sstevel@tonic-gate exit(1); 281*0Sstevel@tonic-gate if (pam_set_item(pamh, PAM_TTY, ttyn) != PAM_SUCCESS) 282*0Sstevel@tonic-gate exit(1); 283*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 284*0Sstevel@tonic-gate 285*0Sstevel@tonic-gate /* 286*0Sstevel@tonic-gate * Save away the following for writing user-level audit records: 287*0Sstevel@tonic-gate * username desired 288*0Sstevel@tonic-gate * current tty 289*0Sstevel@tonic-gate * whether or not username desired is expired 290*0Sstevel@tonic-gate * auditinfo structure of current process 291*0Sstevel@tonic-gate * uid's and gid's of current process 292*0Sstevel@tonic-gate */ 293*0Sstevel@tonic-gate audit_su_init_info(nptr, ttyn); 294*0Sstevel@tonic-gate openlog("su", LOG_CONS, LOG_AUTH); 295*0Sstevel@tonic-gate 296*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 297*0Sstevel@tonic-gate 298*0Sstevel@tonic-gate /* 299*0Sstevel@tonic-gate * Ignore SIGQUIT and SIGINT 300*0Sstevel@tonic-gate */ 301*0Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_IGN); 302*0Sstevel@tonic-gate (void) signal(SIGINT, SIG_IGN); 303*0Sstevel@tonic-gate 304*0Sstevel@tonic-gate /* call pam_authenticate() to authenticate the user through PAM */ 305*0Sstevel@tonic-gate if (getpwnam_r(nptr, &pwd, pwdbuf, sizeof (pwdbuf)) == NULL) 306*0Sstevel@tonic-gate retcode = PAM_USER_UNKNOWN; 307*0Sstevel@tonic-gate else if ((flags = (getuid() != (uid_t)ROOT)) != 0) { 308*0Sstevel@tonic-gate retcode = pam_authenticate(pamh, 0); 309*0Sstevel@tonic-gate } else /* root user does not need to authenticate */ 310*0Sstevel@tonic-gate retcode = PAM_SUCCESS; 311*0Sstevel@tonic-gate 312*0Sstevel@tonic-gate if (retcode != PAM_SUCCESS) { 313*0Sstevel@tonic-gate /* 314*0Sstevel@tonic-gate * Use the same value of sleeptime that login(1) uses. 315*0Sstevel@tonic-gate * This is obtained by reading the file /etc/default/login 316*0Sstevel@tonic-gate * using the def*() functions 317*0Sstevel@tonic-gate */ 318*0Sstevel@tonic-gate if (defopen(DEFAULT_LOGIN) == 0) { 319*0Sstevel@tonic-gate if ((ptr = defread("SLEEPTIME=")) != NULL) 320*0Sstevel@tonic-gate sleeptime = atoi(ptr); 321*0Sstevel@tonic-gate (void) defopen((char *)NULL); 322*0Sstevel@tonic-gate 323*0Sstevel@tonic-gate if (sleeptime < 0 || sleeptime > 5) 324*0Sstevel@tonic-gate sleeptime = SLEEPTIME; 325*0Sstevel@tonic-gate } 326*0Sstevel@tonic-gate 327*0Sstevel@tonic-gate /* 328*0Sstevel@tonic-gate * 1st step: log the error. 329*0Sstevel@tonic-gate * 2nd step: sleep. 330*0Sstevel@tonic-gate * 3rd step: print out message to user. 331*0Sstevel@tonic-gate */ 332*0Sstevel@tonic-gate switch (retcode) { 333*0Sstevel@tonic-gate case PAM_USER_UNKNOWN: 334*0Sstevel@tonic-gate audit_su_bad_username(); 335*0Sstevel@tonic-gate closelog(); 336*0Sstevel@tonic-gate (void) sleep(sleeptime); 337*0Sstevel@tonic-gate message(ERR, gettext("Unknown id: %s"), nptr); 338*0Sstevel@tonic-gate break; 339*0Sstevel@tonic-gate 340*0Sstevel@tonic-gate case PAM_AUTH_ERR: 341*0Sstevel@tonic-gate if (Sulog != NULL) 342*0Sstevel@tonic-gate log(Sulog, nptr, 0); /* log entry */ 343*0Sstevel@tonic-gate audit_su_bad_authentication(); 344*0Sstevel@tonic-gate if (dosyslog) 345*0Sstevel@tonic-gate syslog(LOG_CRIT, "'su %s' failed for %s on %s", 346*0Sstevel@tonic-gate pwd.pw_name, username, ttyn); 347*0Sstevel@tonic-gate closelog(); 348*0Sstevel@tonic-gate (void) sleep(sleeptime); 349*0Sstevel@tonic-gate message(ERR, gettext("Sorry")); 350*0Sstevel@tonic-gate break; 351*0Sstevel@tonic-gate 352*0Sstevel@tonic-gate case PAM_CONV_ERR: 353*0Sstevel@tonic-gate default: 354*0Sstevel@tonic-gate audit_su_unknown_failure(); 355*0Sstevel@tonic-gate if (dosyslog) 356*0Sstevel@tonic-gate syslog(LOG_CRIT, "'su %s' failed for %s on %s", 357*0Sstevel@tonic-gate pwd.pw_name, username, ttyn); 358*0Sstevel@tonic-gate closelog(); 359*0Sstevel@tonic-gate (void) sleep(sleeptime); 360*0Sstevel@tonic-gate message(ERR, gettext("Sorry")); 361*0Sstevel@tonic-gate break; 362*0Sstevel@tonic-gate } 363*0Sstevel@tonic-gate 364*0Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_DFL); 365*0Sstevel@tonic-gate (void) signal(SIGINT, SIG_DFL); 366*0Sstevel@tonic-gate exit(1); 367*0Sstevel@tonic-gate } 368*0Sstevel@tonic-gate if (flags) 369*0Sstevel@tonic-gate validate(username); 370*0Sstevel@tonic-gate if (pam_setcred(pamh, PAM_REINITIALIZE_CRED) != PAM_SUCCESS) { 371*0Sstevel@tonic-gate message(ERR, gettext("unable to set credentials")); 372*0Sstevel@tonic-gate exit(2); 373*0Sstevel@tonic-gate } 374*0Sstevel@tonic-gate audit_su_success(); 375*0Sstevel@tonic-gate if (dosyslog) 376*0Sstevel@tonic-gate syslog(getuid() == 0 ? LOG_INFO : LOG_NOTICE, 377*0Sstevel@tonic-gate "'su %s' succeeded for %s on %s", 378*0Sstevel@tonic-gate pwd.pw_name, username, ttyn); 379*0Sstevel@tonic-gate closelog(); 380*0Sstevel@tonic-gate (void) signal(SIGQUIT, SIG_DFL); 381*0Sstevel@tonic-gate (void) signal(SIGINT, SIG_DFL); 382*0Sstevel@tonic-gate #else /* STATIC !PAM */ 383*0Sstevel@tonic-gate if ((getpwnam_r(nptr, &pwd, pwdbuf, sizeof (pwdbuf)) == NULL) || 384*0Sstevel@tonic-gate (getspnam_r(nptr, &sp, spbuf, sizeof (spbuf)) == NULL)) { 385*0Sstevel@tonic-gate message(ERR, gettext("Unknown id: %s"), nptr); 386*0Sstevel@tonic-gate audit_su_bad_username(); 387*0Sstevel@tonic-gate closelog(); 388*0Sstevel@tonic-gate exit(1); 389*0Sstevel@tonic-gate } 390*0Sstevel@tonic-gate 391*0Sstevel@tonic-gate /* 392*0Sstevel@tonic-gate * Prompt for password if invoking user is not root or 393*0Sstevel@tonic-gate * if specified(new) user requires a password 394*0Sstevel@tonic-gate */ 395*0Sstevel@tonic-gate if (sp.sp_pwdp[0] == '\0' || getuid() == (uid_t)ROOT) 396*0Sstevel@tonic-gate goto ok; 397*0Sstevel@tonic-gate password = getpass(gettext("Password:")); 398*0Sstevel@tonic-gate 399*0Sstevel@tonic-gate if ((strcmp(sp.sp_pwdp, crypt(password, sp.sp_pwdp)) != 0)) { 400*0Sstevel@tonic-gate /* clear password file entry */ 401*0Sstevel@tonic-gate (void) memset((void *)spbuf, 0, sizeof (spbuf)); 402*0Sstevel@tonic-gate if (Sulog != NULL) 403*0Sstevel@tonic-gate log(Sulog, nptr, 0); /* log entry */ 404*0Sstevel@tonic-gate message(ERR, gettext("Sorry")); 405*0Sstevel@tonic-gate audit_su_bad_authentication(); 406*0Sstevel@tonic-gate if (dosyslog) 407*0Sstevel@tonic-gate syslog(LOG_CRIT, "'su %s' failed for %s on %s", 408*0Sstevel@tonic-gate pwd.pw_name, username, ttyn); 409*0Sstevel@tonic-gate closelog(); 410*0Sstevel@tonic-gate exit(2); 411*0Sstevel@tonic-gate } 412*0Sstevel@tonic-gate /* clear password file entry */ 413*0Sstevel@tonic-gate (void) memset((void *)spbuf, 0, sizeof (spbuf)); 414*0Sstevel@tonic-gate ok: 415*0Sstevel@tonic-gate audit_su_reset_ai(); 416*0Sstevel@tonic-gate audit_su_success(); 417*0Sstevel@tonic-gate if (dosyslog) 418*0Sstevel@tonic-gate syslog(getuid() == 0 ? LOG_INFO : LOG_NOTICE, 419*0Sstevel@tonic-gate "'su %s' succeeded for %s on %s", 420*0Sstevel@tonic-gate pwd.pw_name, username, ttyn); 421*0Sstevel@tonic-gate #endif /* STATIC !PAM */ 422*0Sstevel@tonic-gate 423*0Sstevel@tonic-gate uid = pwd.pw_uid; 424*0Sstevel@tonic-gate gid = pwd.pw_gid; 425*0Sstevel@tonic-gate dir = strdup(pwd.pw_dir); 426*0Sstevel@tonic-gate shprog = strdup(pwd.pw_shell); 427*0Sstevel@tonic-gate name = strdup(pwd.pw_name); 428*0Sstevel@tonic-gate 429*0Sstevel@tonic-gate if (Sulog != NULL) 430*0Sstevel@tonic-gate log(Sulog, nptr, 1); /* log entry */ 431*0Sstevel@tonic-gate 432*0Sstevel@tonic-gate /* set user and group ids to specified user */ 433*0Sstevel@tonic-gate 434*0Sstevel@tonic-gate /* set the real (and effective) GID */ 435*0Sstevel@tonic-gate if (setgid(gid) == -1) { 436*0Sstevel@tonic-gate message(ERR, gettext("Invalid GID")); 437*0Sstevel@tonic-gate exit(2); 438*0Sstevel@tonic-gate } 439*0Sstevel@tonic-gate /* Initialize the supplementary group access list. */ 440*0Sstevel@tonic-gate if (!nptr) 441*0Sstevel@tonic-gate exit(2); 442*0Sstevel@tonic-gate if (initgroups(nptr, gid) == -1) { 443*0Sstevel@tonic-gate exit(2); 444*0Sstevel@tonic-gate } 445*0Sstevel@tonic-gate /* set the real (and effective) UID */ 446*0Sstevel@tonic-gate if (setuid(uid) == -1) { 447*0Sstevel@tonic-gate message(ERR, gettext("Invalid UID")); 448*0Sstevel@tonic-gate exit(2); 449*0Sstevel@tonic-gate } 450*0Sstevel@tonic-gate 451*0Sstevel@tonic-gate /* 452*0Sstevel@tonic-gate * If new user's shell field is neither NULL nor equal to /usr/bin/sh, 453*0Sstevel@tonic-gate * set: 454*0Sstevel@tonic-gate * 455*0Sstevel@tonic-gate * pshell = their shell 456*0Sstevel@tonic-gate * su = [-]last component of shell's pathname 457*0Sstevel@tonic-gate * 458*0Sstevel@tonic-gate * Otherwise, set the shell to /usr/bin/sh and set argv[0] to '[-]su'. 459*0Sstevel@tonic-gate */ 460*0Sstevel@tonic-gate if (shprog[0] != '\0' && strcmp(shell, shprog) != 0) { 461*0Sstevel@tonic-gate char *p; 462*0Sstevel@tonic-gate 463*0Sstevel@tonic-gate pshell = shprog; 464*0Sstevel@tonic-gate (void) strcpy(su, eflag ? "-" : ""); 465*0Sstevel@tonic-gate 466*0Sstevel@tonic-gate if ((p = strrchr(pshell, '/')) != NULL) 467*0Sstevel@tonic-gate (void) strlcat(su, p + 1, sizeof (su)); 468*0Sstevel@tonic-gate else 469*0Sstevel@tonic-gate (void) strlcat(su, pshell, sizeof (su)); 470*0Sstevel@tonic-gate } else { 471*0Sstevel@tonic-gate pshell = shell; 472*0Sstevel@tonic-gate (void) strcpy(su, eflag ? "-su" : "su"); 473*0Sstevel@tonic-gate } 474*0Sstevel@tonic-gate 475*0Sstevel@tonic-gate /* 476*0Sstevel@tonic-gate * set environment variables for new user; 477*0Sstevel@tonic-gate * arg0 for exec of shprog must now contain `-' 478*0Sstevel@tonic-gate * so that environment of new user is given 479*0Sstevel@tonic-gate */ 480*0Sstevel@tonic-gate if (eflag) { 481*0Sstevel@tonic-gate int j; 482*0Sstevel@tonic-gate char *var; 483*0Sstevel@tonic-gate 484*0Sstevel@tonic-gate if (strlen(dir) == 0) { 485*0Sstevel@tonic-gate (void) strcpy(dir, "/"); 486*0Sstevel@tonic-gate message(WARN, gettext("No directory! Using home=/")); 487*0Sstevel@tonic-gate } 488*0Sstevel@tonic-gate (void) strlcat(homedir, dir, sizeof (homedir)); 489*0Sstevel@tonic-gate (void) strlcat(logname, name, sizeof (logname)); 490*0Sstevel@tonic-gate if (hz = getenv("HZ")) 491*0Sstevel@tonic-gate (void) strlcat(hzname, hz, sizeof (hzname)); 492*0Sstevel@tonic-gate 493*0Sstevel@tonic-gate (void) strlcat(shelltyp, pshell, sizeof (shelltyp)); 494*0Sstevel@tonic-gate 495*0Sstevel@tonic-gate if (chdir(dir) < 0) { 496*0Sstevel@tonic-gate message(ERR, gettext("No directory!")); 497*0Sstevel@tonic-gate exit(1); 498*0Sstevel@tonic-gate } 499*0Sstevel@tonic-gate envinit[envidx = 0] = homedir; 500*0Sstevel@tonic-gate envinit[++envidx] = ((uid == (uid_t)ROOT) ? supath : path); 501*0Sstevel@tonic-gate envinit[++envidx] = logname; 502*0Sstevel@tonic-gate envinit[++envidx] = hzname; 503*0Sstevel@tonic-gate if ((term = getenv("TERM")) != NULL) { 504*0Sstevel@tonic-gate (void) strlcat(termtyp, term, sizeof (termtyp)); 505*0Sstevel@tonic-gate envinit[++envidx] = termtyp; 506*0Sstevel@tonic-gate } 507*0Sstevel@tonic-gate envinit[++envidx] = shelltyp; 508*0Sstevel@tonic-gate 509*0Sstevel@tonic-gate (void) strlcat(mail, name, sizeof (mail)); 510*0Sstevel@tonic-gate envinit[++envidx] = mail; 511*0Sstevel@tonic-gate 512*0Sstevel@tonic-gate /* 513*0Sstevel@tonic-gate * Fetch the relevant locale/TZ environment variables from 514*0Sstevel@tonic-gate * the inherited environment. 515*0Sstevel@tonic-gate * 516*0Sstevel@tonic-gate * We have a priority here for setting TZ. If TZ is set in 517*0Sstevel@tonic-gate * in the inherited environment, that value remains top 518*0Sstevel@tonic-gate * priority. If the file /etc/default/login has TIMEZONE set, 519*0Sstevel@tonic-gate * that has second highest priority. 520*0Sstevel@tonic-gate */ 521*0Sstevel@tonic-gate tznam[0] = '\0'; 522*0Sstevel@tonic-gate for (j = 0; initenv[j] != 0; j++) { 523*0Sstevel@tonic-gate if (initvar = getenv(initenv[j])) { 524*0Sstevel@tonic-gate 525*0Sstevel@tonic-gate /* 526*0Sstevel@tonic-gate * Skip over values beginning with '/' for 527*0Sstevel@tonic-gate * security. 528*0Sstevel@tonic-gate */ 529*0Sstevel@tonic-gate if (initvar[0] == '/') continue; 530*0Sstevel@tonic-gate 531*0Sstevel@tonic-gate if (strcmp(initenv[j], "TZ") == 0) { 532*0Sstevel@tonic-gate (void) strcpy(tznam, "TZ="); 533*0Sstevel@tonic-gate (void) strlcat(tznam, initvar, 534*0Sstevel@tonic-gate sizeof (tznam)); 535*0Sstevel@tonic-gate 536*0Sstevel@tonic-gate } else { 537*0Sstevel@tonic-gate var = (char *) 538*0Sstevel@tonic-gate malloc(strlen(initenv[j]) 539*0Sstevel@tonic-gate + strlen(initvar) 540*0Sstevel@tonic-gate + 2); 541*0Sstevel@tonic-gate (void) strcpy(var, initenv[j]); 542*0Sstevel@tonic-gate (void) strcat(var, "="); 543*0Sstevel@tonic-gate (void) strcat(var, initvar); 544*0Sstevel@tonic-gate envinit[++envidx] = var; 545*0Sstevel@tonic-gate } 546*0Sstevel@tonic-gate } 547*0Sstevel@tonic-gate } 548*0Sstevel@tonic-gate 549*0Sstevel@tonic-gate /* 550*0Sstevel@tonic-gate * Check if TZ was found. If not then try to read it from 551*0Sstevel@tonic-gate * /etc/default/login. 552*0Sstevel@tonic-gate */ 553*0Sstevel@tonic-gate if (tznam[0] == '\0') { 554*0Sstevel@tonic-gate if (defopen(DEFAULT_LOGIN) == 0) { 555*0Sstevel@tonic-gate if (initvar = defread("TIMEZONE=")) { 556*0Sstevel@tonic-gate (void) strcpy(tznam, "TZ="); 557*0Sstevel@tonic-gate (void) strlcat(tznam, initvar, 558*0Sstevel@tonic-gate sizeof (tznam)); 559*0Sstevel@tonic-gate } 560*0Sstevel@tonic-gate (void) defopen(NULL); 561*0Sstevel@tonic-gate } 562*0Sstevel@tonic-gate } 563*0Sstevel@tonic-gate 564*0Sstevel@tonic-gate if (tznam[0] != '\0') 565*0Sstevel@tonic-gate envinit[++envidx] = tznam; 566*0Sstevel@tonic-gate 567*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 568*0Sstevel@tonic-gate /* 569*0Sstevel@tonic-gate * set the PAM environment variables - 570*0Sstevel@tonic-gate * check for legal environment variables 571*0Sstevel@tonic-gate */ 572*0Sstevel@tonic-gate if ((pam_env = pam_getenvlist(pamh)) != 0) { 573*0Sstevel@tonic-gate while (pam_env[idx] != 0) { 574*0Sstevel@tonic-gate if (envidx + 2 < ELIM && 575*0Sstevel@tonic-gate legalenvvar(pam_env[idx])) { 576*0Sstevel@tonic-gate envinit[++envidx] = pam_env[idx]; 577*0Sstevel@tonic-gate } 578*0Sstevel@tonic-gate idx++; 579*0Sstevel@tonic-gate } 580*0Sstevel@tonic-gate } 581*0Sstevel@tonic-gate #endif 582*0Sstevel@tonic-gate envinit[++envidx] = NULL; 583*0Sstevel@tonic-gate environ = envinit; 584*0Sstevel@tonic-gate } else { 585*0Sstevel@tonic-gate char **pp = environ, **qq, *p; 586*0Sstevel@tonic-gate 587*0Sstevel@tonic-gate while ((p = *pp) != NULL) { 588*0Sstevel@tonic-gate if (*p == 'L' && p[1] == 'D' && p[2] == '_') { 589*0Sstevel@tonic-gate for (qq = pp; (*qq = qq[1]) != NULL; qq++) 590*0Sstevel@tonic-gate ; 591*0Sstevel@tonic-gate /* pp is not advanced */ 592*0Sstevel@tonic-gate } else { 593*0Sstevel@tonic-gate pp++; 594*0Sstevel@tonic-gate } 595*0Sstevel@tonic-gate } 596*0Sstevel@tonic-gate } 597*0Sstevel@tonic-gate 598*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 599*0Sstevel@tonic-gate if (pamh) 600*0Sstevel@tonic-gate (void) pam_end(pamh, PAM_SUCCESS); 601*0Sstevel@tonic-gate #endif 602*0Sstevel@tonic-gate 603*0Sstevel@tonic-gate /* 604*0Sstevel@tonic-gate * if new user is root: 605*0Sstevel@tonic-gate * if CONSOLE defined, log entry there; 606*0Sstevel@tonic-gate * if eflag not set, change environment to that of root. 607*0Sstevel@tonic-gate */ 608*0Sstevel@tonic-gate if (uid == (uid_t)ROOT) { 609*0Sstevel@tonic-gate if (Console != NULL) 610*0Sstevel@tonic-gate if (strcmp(ttyn, Console) != 0) { 611*0Sstevel@tonic-gate (void) signal(SIGALRM, to); 612*0Sstevel@tonic-gate (void) alarm(30); 613*0Sstevel@tonic-gate log(Console, nptr, 1); 614*0Sstevel@tonic-gate (void) alarm(0); 615*0Sstevel@tonic-gate } 616*0Sstevel@tonic-gate if (!eflag) 617*0Sstevel@tonic-gate envalt(); 618*0Sstevel@tonic-gate } 619*0Sstevel@tonic-gate 620*0Sstevel@tonic-gate /* 621*0Sstevel@tonic-gate * Default for SIGCPU and SIGXFSZ. Shells inherit 622*0Sstevel@tonic-gate * signal disposition from parent. And the 623*0Sstevel@tonic-gate * shells should have default dispositions for these 624*0Sstevel@tonic-gate * signals. 625*0Sstevel@tonic-gate */ 626*0Sstevel@tonic-gate (void) signal(SIGXCPU, SIG_DFL); 627*0Sstevel@tonic-gate (void) signal(SIGXFSZ, SIG_DFL); 628*0Sstevel@tonic-gate 629*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 630*0Sstevel@tonic-gate if (embedded) { 631*0Sstevel@tonic-gate (void) puts("SUCCESS"); 632*0Sstevel@tonic-gate /* 633*0Sstevel@tonic-gate * After this point, we're no longer talking the 634*0Sstevel@tonic-gate * embedded_su protocol, so turn it off. 635*0Sstevel@tonic-gate */ 636*0Sstevel@tonic-gate embedded = B_FALSE; 637*0Sstevel@tonic-gate } 638*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 639*0Sstevel@tonic-gate 640*0Sstevel@tonic-gate /* 641*0Sstevel@tonic-gate * if additional arguments, exec shell program with array 642*0Sstevel@tonic-gate * of pointers to arguments: 643*0Sstevel@tonic-gate * -> if shell = default, then su = [-]su 644*0Sstevel@tonic-gate * -> if shell != default, then su = [-]last component of 645*0Sstevel@tonic-gate * shell's pathname 646*0Sstevel@tonic-gate * 647*0Sstevel@tonic-gate * if no additional arguments, exec shell with arg0 of su 648*0Sstevel@tonic-gate * where: 649*0Sstevel@tonic-gate * -> if shell = default, then su = [-]su 650*0Sstevel@tonic-gate * -> if shell != default, then su = [-]last component of 651*0Sstevel@tonic-gate * shell's pathname 652*0Sstevel@tonic-gate */ 653*0Sstevel@tonic-gate if (argc > 2) { 654*0Sstevel@tonic-gate argv[1] = su; 655*0Sstevel@tonic-gate (void) execv(pshell, &argv[1]); 656*0Sstevel@tonic-gate } else 657*0Sstevel@tonic-gate (void) execl(pshell, su, 0); 658*0Sstevel@tonic-gate 659*0Sstevel@tonic-gate 660*0Sstevel@tonic-gate /* 661*0Sstevel@tonic-gate * Try to clean up after an administrator who has made a mistake 662*0Sstevel@tonic-gate * configuring root's shell; if root's shell is other than /sbin/sh, 663*0Sstevel@tonic-gate * try exec'ing /sbin/sh instead. 664*0Sstevel@tonic-gate */ 665*0Sstevel@tonic-gate if ((uid == (uid_t)ROOT) && (strcmp(name, "root") == 0) && 666*0Sstevel@tonic-gate (strcmp(safe_shell, pshell) != 0)) { 667*0Sstevel@tonic-gate message(WARN, 668*0Sstevel@tonic-gate gettext("No shell %s. Trying fallback shell %s."), 669*0Sstevel@tonic-gate pshell, safe_shell); 670*0Sstevel@tonic-gate 671*0Sstevel@tonic-gate if (eflag) { 672*0Sstevel@tonic-gate (void) strcpy(su, "-sh"); 673*0Sstevel@tonic-gate (void) strlcpy(shelltyp + strlen("SHELL="), 674*0Sstevel@tonic-gate safe_shell, sizeof (shelltyp) - strlen("SHELL=")); 675*0Sstevel@tonic-gate } else { 676*0Sstevel@tonic-gate (void) strcpy(su, "sh"); 677*0Sstevel@tonic-gate } 678*0Sstevel@tonic-gate 679*0Sstevel@tonic-gate if (argc > 2) { 680*0Sstevel@tonic-gate argv[1] = su; 681*0Sstevel@tonic-gate (void) execv(safe_shell, &argv[1]); 682*0Sstevel@tonic-gate } else { 683*0Sstevel@tonic-gate (void) execl(safe_shell, su, 0); 684*0Sstevel@tonic-gate } 685*0Sstevel@tonic-gate message(ERR, gettext("Couldn't exec fallback shell %s: %s"), 686*0Sstevel@tonic-gate safe_shell, strerror(errno)); 687*0Sstevel@tonic-gate } else { 688*0Sstevel@tonic-gate message(ERR, gettext("No shell")); 689*0Sstevel@tonic-gate } 690*0Sstevel@tonic-gate return (3); 691*0Sstevel@tonic-gate } 692*0Sstevel@tonic-gate 693*0Sstevel@tonic-gate /* 694*0Sstevel@tonic-gate * Environment altering routine - 695*0Sstevel@tonic-gate * This routine is called when a user is su'ing to root 696*0Sstevel@tonic-gate * without specifying the - flag. 697*0Sstevel@tonic-gate * The user's PATH and PS1 variables are reset 698*0Sstevel@tonic-gate * to the correct value for root. 699*0Sstevel@tonic-gate * All of the user's other environment variables retain 700*0Sstevel@tonic-gate * their current values after the su (if they are exported). 701*0Sstevel@tonic-gate */ 702*0Sstevel@tonic-gate static void 703*0Sstevel@tonic-gate envalt(void) 704*0Sstevel@tonic-gate { 705*0Sstevel@tonic-gate /* 706*0Sstevel@tonic-gate * If user has PATH variable in their environment, change its value 707*0Sstevel@tonic-gate * to /bin:/etc:/usr/bin ; 708*0Sstevel@tonic-gate * if user does not have PATH variable, add it to the user's 709*0Sstevel@tonic-gate * environment; 710*0Sstevel@tonic-gate * if either of the above fail, an error message is printed. 711*0Sstevel@tonic-gate */ 712*0Sstevel@tonic-gate if (putenv(supath) != 0) { 713*0Sstevel@tonic-gate message(ERR, 714*0Sstevel@tonic-gate gettext("unable to obtain memory to expand environment")); 715*0Sstevel@tonic-gate exit(4); 716*0Sstevel@tonic-gate } 717*0Sstevel@tonic-gate 718*0Sstevel@tonic-gate /* 719*0Sstevel@tonic-gate * If user has PROMPT variable in their environment, change its value 720*0Sstevel@tonic-gate * to # ; 721*0Sstevel@tonic-gate * if user does not have PROMPT variable, add it to the user's 722*0Sstevel@tonic-gate * environment; 723*0Sstevel@tonic-gate * if either of the above fail, an error message is printed. 724*0Sstevel@tonic-gate */ 725*0Sstevel@tonic-gate if (putenv(suprmt) != 0) { 726*0Sstevel@tonic-gate message(ERR, 727*0Sstevel@tonic-gate gettext("unable to obtain memory to expand environment")); 728*0Sstevel@tonic-gate exit(4); 729*0Sstevel@tonic-gate } 730*0Sstevel@tonic-gate } 731*0Sstevel@tonic-gate 732*0Sstevel@tonic-gate /* 733*0Sstevel@tonic-gate * Logging routine - 734*0Sstevel@tonic-gate * where = SULOG or CONSOLE 735*0Sstevel@tonic-gate * towho = specified user ( user being su'ed to ) 736*0Sstevel@tonic-gate * how = 0 if su attempt failed; 1 if su attempt succeeded 737*0Sstevel@tonic-gate */ 738*0Sstevel@tonic-gate static void 739*0Sstevel@tonic-gate log(char *where, char *towho, int how) 740*0Sstevel@tonic-gate { 741*0Sstevel@tonic-gate FILE *logf; 742*0Sstevel@tonic-gate time_t now; 743*0Sstevel@tonic-gate struct tm *tmp; 744*0Sstevel@tonic-gate 745*0Sstevel@tonic-gate /* 746*0Sstevel@tonic-gate * open SULOG or CONSOLE - if open fails, return 747*0Sstevel@tonic-gate */ 748*0Sstevel@tonic-gate if ((logf = fopen(where, "a")) == NULL) 749*0Sstevel@tonic-gate return; 750*0Sstevel@tonic-gate 751*0Sstevel@tonic-gate now = time(0); 752*0Sstevel@tonic-gate tmp = localtime(&now); 753*0Sstevel@tonic-gate 754*0Sstevel@tonic-gate /* 755*0Sstevel@tonic-gate * write entry into SULOG or onto CONSOLE - if write fails, return 756*0Sstevel@tonic-gate */ 757*0Sstevel@tonic-gate (void) fprintf(logf, "SU %.2d/%.2d %.2d:%.2d %c %s %s-%s\n", 758*0Sstevel@tonic-gate tmp->tm_mon + 1, tmp->tm_mday, tmp->tm_hour, tmp->tm_min, 759*0Sstevel@tonic-gate how ? '+' : '-', ttyn + sizeof ("/dev/") - 1, username, towho); 760*0Sstevel@tonic-gate 761*0Sstevel@tonic-gate (void) fclose(logf); /* close SULOG or CONSOLE */ 762*0Sstevel@tonic-gate } 763*0Sstevel@tonic-gate 764*0Sstevel@tonic-gate /*ARGSUSED*/ 765*0Sstevel@tonic-gate static void 766*0Sstevel@tonic-gate to(int sig) 767*0Sstevel@tonic-gate {} 768*0Sstevel@tonic-gate 769*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 770*0Sstevel@tonic-gate /* 771*0Sstevel@tonic-gate * su_conv(): 772*0Sstevel@tonic-gate * This is the conv (conversation) function called from 773*0Sstevel@tonic-gate * a PAM authentication module to print error messages 774*0Sstevel@tonic-gate * or garner information from the user. 775*0Sstevel@tonic-gate */ 776*0Sstevel@tonic-gate /*ARGSUSED*/ 777*0Sstevel@tonic-gate static int 778*0Sstevel@tonic-gate su_conv(int num_msg, struct pam_message **msg, struct pam_response **response, 779*0Sstevel@tonic-gate void *appdata_ptr) 780*0Sstevel@tonic-gate { 781*0Sstevel@tonic-gate struct pam_message *m; 782*0Sstevel@tonic-gate struct pam_response *r; 783*0Sstevel@tonic-gate char *temp; 784*0Sstevel@tonic-gate int k; 785*0Sstevel@tonic-gate char respbuf[PAM_MAX_RESP_SIZE]; 786*0Sstevel@tonic-gate 787*0Sstevel@tonic-gate if (num_msg <= 0) 788*0Sstevel@tonic-gate return (PAM_CONV_ERR); 789*0Sstevel@tonic-gate 790*0Sstevel@tonic-gate *response = (struct pam_response *)calloc(num_msg, 791*0Sstevel@tonic-gate sizeof (struct pam_response)); 792*0Sstevel@tonic-gate if (*response == NULL) 793*0Sstevel@tonic-gate return (PAM_BUF_ERR); 794*0Sstevel@tonic-gate 795*0Sstevel@tonic-gate k = num_msg; 796*0Sstevel@tonic-gate m = *msg; 797*0Sstevel@tonic-gate r = *response; 798*0Sstevel@tonic-gate while (k--) { 799*0Sstevel@tonic-gate 800*0Sstevel@tonic-gate switch (m->msg_style) { 801*0Sstevel@tonic-gate 802*0Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF: 803*0Sstevel@tonic-gate errno = 0; 804*0Sstevel@tonic-gate temp = getpassphrase(m->msg); 805*0Sstevel@tonic-gate if (errno == EINTR) 806*0Sstevel@tonic-gate return (PAM_CONV_ERR); 807*0Sstevel@tonic-gate if (temp != NULL) { 808*0Sstevel@tonic-gate r->resp = strdup(temp); 809*0Sstevel@tonic-gate if (r->resp == NULL) { 810*0Sstevel@tonic-gate freeresponse(num_msg, response); 811*0Sstevel@tonic-gate return (PAM_BUF_ERR); 812*0Sstevel@tonic-gate } 813*0Sstevel@tonic-gate } 814*0Sstevel@tonic-gate break; 815*0Sstevel@tonic-gate 816*0Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON: 817*0Sstevel@tonic-gate if (m->msg != NULL) { 818*0Sstevel@tonic-gate (void) fputs(m->msg, stdout); 819*0Sstevel@tonic-gate } 820*0Sstevel@tonic-gate 821*0Sstevel@tonic-gate (void) fgets(respbuf, sizeof (respbuf), stdin); 822*0Sstevel@tonic-gate temp = strchr(respbuf, '\n'); 823*0Sstevel@tonic-gate if (temp != NULL) 824*0Sstevel@tonic-gate *temp = '\0'; 825*0Sstevel@tonic-gate 826*0Sstevel@tonic-gate r->resp = strdup(respbuf); 827*0Sstevel@tonic-gate if (r->resp == NULL) { 828*0Sstevel@tonic-gate freeresponse(num_msg, response); 829*0Sstevel@tonic-gate return (PAM_BUF_ERR); 830*0Sstevel@tonic-gate } 831*0Sstevel@tonic-gate break; 832*0Sstevel@tonic-gate 833*0Sstevel@tonic-gate case PAM_ERROR_MSG: 834*0Sstevel@tonic-gate if (m->msg != NULL) { 835*0Sstevel@tonic-gate (void) fputs(m->msg, stderr); 836*0Sstevel@tonic-gate (void) fputs("\n", stderr); 837*0Sstevel@tonic-gate } 838*0Sstevel@tonic-gate break; 839*0Sstevel@tonic-gate 840*0Sstevel@tonic-gate case PAM_TEXT_INFO: 841*0Sstevel@tonic-gate if (m->msg != NULL) { 842*0Sstevel@tonic-gate (void) fputs(m->msg, stdout); 843*0Sstevel@tonic-gate (void) fputs("\n", stdout); 844*0Sstevel@tonic-gate } 845*0Sstevel@tonic-gate break; 846*0Sstevel@tonic-gate 847*0Sstevel@tonic-gate default: 848*0Sstevel@tonic-gate break; 849*0Sstevel@tonic-gate } 850*0Sstevel@tonic-gate m++; 851*0Sstevel@tonic-gate r++; 852*0Sstevel@tonic-gate } 853*0Sstevel@tonic-gate return (PAM_SUCCESS); 854*0Sstevel@tonic-gate } 855*0Sstevel@tonic-gate 856*0Sstevel@tonic-gate /* 857*0Sstevel@tonic-gate * emb_su_conv(): 858*0Sstevel@tonic-gate * This is the conv (conversation) function called from 859*0Sstevel@tonic-gate * a PAM authentication module to print error messages 860*0Sstevel@tonic-gate * or garner information from the user. 861*0Sstevel@tonic-gate * This version is used for embedded_su. 862*0Sstevel@tonic-gate */ 863*0Sstevel@tonic-gate /*ARGSUSED*/ 864*0Sstevel@tonic-gate static int 865*0Sstevel@tonic-gate emb_su_conv(int num_msg, struct pam_message **msg, 866*0Sstevel@tonic-gate struct pam_response **response, void *appdata_ptr) 867*0Sstevel@tonic-gate { 868*0Sstevel@tonic-gate struct pam_message *m; 869*0Sstevel@tonic-gate struct pam_response *r; 870*0Sstevel@tonic-gate char *temp; 871*0Sstevel@tonic-gate int k; 872*0Sstevel@tonic-gate char respbuf[PAM_MAX_RESP_SIZE]; 873*0Sstevel@tonic-gate 874*0Sstevel@tonic-gate if (num_msg <= 0) 875*0Sstevel@tonic-gate return (PAM_CONV_ERR); 876*0Sstevel@tonic-gate 877*0Sstevel@tonic-gate *response = (struct pam_response *)calloc(num_msg, 878*0Sstevel@tonic-gate sizeof (struct pam_response)); 879*0Sstevel@tonic-gate if (*response == NULL) 880*0Sstevel@tonic-gate return (PAM_BUF_ERR); 881*0Sstevel@tonic-gate 882*0Sstevel@tonic-gate /* First, send the prompts */ 883*0Sstevel@tonic-gate (void) printf("CONV %d\n", num_msg); 884*0Sstevel@tonic-gate k = num_msg; 885*0Sstevel@tonic-gate m = *msg; 886*0Sstevel@tonic-gate while (k--) { 887*0Sstevel@tonic-gate switch (m->msg_style) { 888*0Sstevel@tonic-gate 889*0Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF: 890*0Sstevel@tonic-gate (void) puts("PAM_PROMPT_ECHO_OFF"); 891*0Sstevel@tonic-gate goto msg_common; 892*0Sstevel@tonic-gate 893*0Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON: 894*0Sstevel@tonic-gate (void) puts("PAM_PROMPT_ECHO_ON"); 895*0Sstevel@tonic-gate goto msg_common; 896*0Sstevel@tonic-gate 897*0Sstevel@tonic-gate case PAM_ERROR_MSG: 898*0Sstevel@tonic-gate (void) puts("PAM_ERROR_MSG"); 899*0Sstevel@tonic-gate goto msg_common; 900*0Sstevel@tonic-gate 901*0Sstevel@tonic-gate case PAM_TEXT_INFO: 902*0Sstevel@tonic-gate (void) puts("PAM_TEXT_INFO"); 903*0Sstevel@tonic-gate /* fall through to msg_common */ 904*0Sstevel@tonic-gate msg_common: 905*0Sstevel@tonic-gate if (m->msg == NULL) 906*0Sstevel@tonic-gate quotemsg(NULL); 907*0Sstevel@tonic-gate else 908*0Sstevel@tonic-gate quotemsg("%s", m->msg); 909*0Sstevel@tonic-gate break; 910*0Sstevel@tonic-gate 911*0Sstevel@tonic-gate default: 912*0Sstevel@tonic-gate break; 913*0Sstevel@tonic-gate } 914*0Sstevel@tonic-gate m++; 915*0Sstevel@tonic-gate } 916*0Sstevel@tonic-gate 917*0Sstevel@tonic-gate /* Next, collect the responses */ 918*0Sstevel@tonic-gate k = num_msg; 919*0Sstevel@tonic-gate m = *msg; 920*0Sstevel@tonic-gate r = *response; 921*0Sstevel@tonic-gate while (k--) { 922*0Sstevel@tonic-gate 923*0Sstevel@tonic-gate switch (m->msg_style) { 924*0Sstevel@tonic-gate 925*0Sstevel@tonic-gate case PAM_PROMPT_ECHO_OFF: 926*0Sstevel@tonic-gate case PAM_PROMPT_ECHO_ON: 927*0Sstevel@tonic-gate (void) fgets(respbuf, sizeof (respbuf), stdin); 928*0Sstevel@tonic-gate 929*0Sstevel@tonic-gate temp = strchr(respbuf, '\n'); 930*0Sstevel@tonic-gate if (temp != NULL) 931*0Sstevel@tonic-gate *temp = '\0'; 932*0Sstevel@tonic-gate 933*0Sstevel@tonic-gate r->resp = strdup(respbuf); 934*0Sstevel@tonic-gate if (r->resp == NULL) { 935*0Sstevel@tonic-gate freeresponse(num_msg, response); 936*0Sstevel@tonic-gate return (PAM_BUF_ERR); 937*0Sstevel@tonic-gate } 938*0Sstevel@tonic-gate 939*0Sstevel@tonic-gate break; 940*0Sstevel@tonic-gate 941*0Sstevel@tonic-gate case PAM_ERROR_MSG: 942*0Sstevel@tonic-gate case PAM_TEXT_INFO: 943*0Sstevel@tonic-gate break; 944*0Sstevel@tonic-gate 945*0Sstevel@tonic-gate default: 946*0Sstevel@tonic-gate break; 947*0Sstevel@tonic-gate } 948*0Sstevel@tonic-gate m++; 949*0Sstevel@tonic-gate r++; 950*0Sstevel@tonic-gate } 951*0Sstevel@tonic-gate return (PAM_SUCCESS); 952*0Sstevel@tonic-gate } 953*0Sstevel@tonic-gate 954*0Sstevel@tonic-gate static void 955*0Sstevel@tonic-gate freeresponse(int num_msg, struct pam_response **response) 956*0Sstevel@tonic-gate { 957*0Sstevel@tonic-gate struct pam_response *r; 958*0Sstevel@tonic-gate int i; 959*0Sstevel@tonic-gate 960*0Sstevel@tonic-gate /* free responses */ 961*0Sstevel@tonic-gate r = *response; 962*0Sstevel@tonic-gate for (i = 0; i < num_msg; i++, r++) { 963*0Sstevel@tonic-gate if (r->resp != NULL) { 964*0Sstevel@tonic-gate /* Zap it in case it's a password */ 965*0Sstevel@tonic-gate (void) memset(r->resp, '\0', strlen(r->resp)); 966*0Sstevel@tonic-gate free(r->resp); 967*0Sstevel@tonic-gate } 968*0Sstevel@tonic-gate } 969*0Sstevel@tonic-gate free(*response); 970*0Sstevel@tonic-gate *response = NULL; 971*0Sstevel@tonic-gate } 972*0Sstevel@tonic-gate 973*0Sstevel@tonic-gate /* 974*0Sstevel@tonic-gate * Print a message, applying quoting for lines starting with '.'. 975*0Sstevel@tonic-gate * 976*0Sstevel@tonic-gate * I18n note: \n is "safe" in all locales, and all locales use 977*0Sstevel@tonic-gate * a high-bit-set character to start multibyte sequences, so 978*0Sstevel@tonic-gate * scanning for a \n followed by a '.' is safe. 979*0Sstevel@tonic-gate */ 980*0Sstevel@tonic-gate static void 981*0Sstevel@tonic-gate quotemsg(char *fmt, ...) 982*0Sstevel@tonic-gate { 983*0Sstevel@tonic-gate if (fmt != NULL) { 984*0Sstevel@tonic-gate char *msg; 985*0Sstevel@tonic-gate char *p; 986*0Sstevel@tonic-gate boolean_t bol; 987*0Sstevel@tonic-gate va_list v; 988*0Sstevel@tonic-gate 989*0Sstevel@tonic-gate va_start(v, fmt); 990*0Sstevel@tonic-gate msg = alloc_vsprintf(fmt, v); 991*0Sstevel@tonic-gate va_end(v); 992*0Sstevel@tonic-gate 993*0Sstevel@tonic-gate bol = B_TRUE; 994*0Sstevel@tonic-gate for (p = msg; *p != '\0'; p++) { 995*0Sstevel@tonic-gate if (bol) { 996*0Sstevel@tonic-gate if (*p == '.') 997*0Sstevel@tonic-gate (void) putchar('.'); 998*0Sstevel@tonic-gate bol = B_FALSE; 999*0Sstevel@tonic-gate } 1000*0Sstevel@tonic-gate (void) putchar(*p); 1001*0Sstevel@tonic-gate if (*p == '\n') 1002*0Sstevel@tonic-gate bol = B_TRUE; 1003*0Sstevel@tonic-gate } 1004*0Sstevel@tonic-gate (void) putchar('\n'); 1005*0Sstevel@tonic-gate free(msg); 1006*0Sstevel@tonic-gate } 1007*0Sstevel@tonic-gate (void) putchar('.'); 1008*0Sstevel@tonic-gate (void) putchar('\n'); 1009*0Sstevel@tonic-gate } 1010*0Sstevel@tonic-gate 1011*0Sstevel@tonic-gate /* 1012*0Sstevel@tonic-gate * validate - Check that the account is valid for switching to. 1013*0Sstevel@tonic-gate * 1014*0Sstevel@tonic-gate * If the password has expired, we must refuse the 'su' attempt 1015*0Sstevel@tonic-gate * regardless of whether the password is null or not. 1016*0Sstevel@tonic-gate * 1017*0Sstevel@tonic-gate * If the password is NULL but has NOT expired then we allow the 1018*0Sstevel@tonic-gate * su attempt to succeed. 1019*0Sstevel@tonic-gate */ 1020*0Sstevel@tonic-gate static void 1021*0Sstevel@tonic-gate validate(char *usernam) 1022*0Sstevel@tonic-gate { 1023*0Sstevel@tonic-gate int error = 0; 1024*0Sstevel@tonic-gate 1025*0Sstevel@tonic-gate if ((error = pam_acct_mgmt(pamh, 0)) != 0) { 1026*0Sstevel@tonic-gate if (Sulog != NULL) 1027*0Sstevel@tonic-gate log(Sulog, pwd.pw_name, 0); /* log entry */ 1028*0Sstevel@tonic-gate if (error == PAM_NEW_AUTHTOK_REQD) { 1029*0Sstevel@tonic-gate message(ERR, gettext("Password for user " 1030*0Sstevel@tonic-gate "'%s' has expired - use passwd(1) to update it"), 1031*0Sstevel@tonic-gate pwd.pw_name); 1032*0Sstevel@tonic-gate audit_su_bad_authentication(); 1033*0Sstevel@tonic-gate if (dosyslog) 1034*0Sstevel@tonic-gate syslog(LOG_CRIT, "'su %s' failed for %s on %s", 1035*0Sstevel@tonic-gate pwd.pw_name, usernam, ttyn); 1036*0Sstevel@tonic-gate closelog(); 1037*0Sstevel@tonic-gate exit(1); 1038*0Sstevel@tonic-gate } else { 1039*0Sstevel@tonic-gate message(ERR, gettext("Sorry")); 1040*0Sstevel@tonic-gate audit_su_bad_authentication(); 1041*0Sstevel@tonic-gate if (dosyslog) 1042*0Sstevel@tonic-gate syslog(LOG_CRIT, "'su %s' failed for %s on %s", 1043*0Sstevel@tonic-gate pwd.pw_name, usernam, ttyn); 1044*0Sstevel@tonic-gate closelog(); 1045*0Sstevel@tonic-gate exit(3); 1046*0Sstevel@tonic-gate } 1047*0Sstevel@tonic-gate } 1048*0Sstevel@tonic-gate } 1049*0Sstevel@tonic-gate 1050*0Sstevel@tonic-gate static char *illegal[] = { 1051*0Sstevel@tonic-gate "SHELL=", 1052*0Sstevel@tonic-gate "HOME=", 1053*0Sstevel@tonic-gate "LOGNAME=", 1054*0Sstevel@tonic-gate #ifndef NO_MAIL 1055*0Sstevel@tonic-gate "MAIL=", 1056*0Sstevel@tonic-gate #endif 1057*0Sstevel@tonic-gate "CDPATH=", 1058*0Sstevel@tonic-gate "IFS=", 1059*0Sstevel@tonic-gate "PATH=", 1060*0Sstevel@tonic-gate "TZ=", 1061*0Sstevel@tonic-gate "HZ=", 1062*0Sstevel@tonic-gate "TERM=", 1063*0Sstevel@tonic-gate 0 1064*0Sstevel@tonic-gate }; 1065*0Sstevel@tonic-gate 1066*0Sstevel@tonic-gate /* 1067*0Sstevel@tonic-gate * legalenvvar - can PAM modules insert this environmental variable? 1068*0Sstevel@tonic-gate */ 1069*0Sstevel@tonic-gate 1070*0Sstevel@tonic-gate static int 1071*0Sstevel@tonic-gate legalenvvar(char *s) 1072*0Sstevel@tonic-gate { 1073*0Sstevel@tonic-gate register char **p; 1074*0Sstevel@tonic-gate 1075*0Sstevel@tonic-gate for (p = illegal; *p; p++) 1076*0Sstevel@tonic-gate if (strncmp(s, *p, strlen(*p)) == 0) 1077*0Sstevel@tonic-gate return (0); 1078*0Sstevel@tonic-gate 1079*0Sstevel@tonic-gate if (s[0] == 'L' && s[1] == 'D' && s[2] == '_') 1080*0Sstevel@tonic-gate return (0); 1081*0Sstevel@tonic-gate 1082*0Sstevel@tonic-gate return (1); 1083*0Sstevel@tonic-gate } 1084*0Sstevel@tonic-gate 1085*0Sstevel@tonic-gate /* 1086*0Sstevel@tonic-gate * The embedded_su protocol allows the client application to supply 1087*0Sstevel@tonic-gate * an initialization block terminated by a line with just a "." on it. 1088*0Sstevel@tonic-gate * 1089*0Sstevel@tonic-gate * This initialization block is currently unused, reserved for future 1090*0Sstevel@tonic-gate * expansion. Ignore it. This is made very slightly more complex by 1091*0Sstevel@tonic-gate * the desire to cleanly ignore input lines of any length, while still 1092*0Sstevel@tonic-gate * correctly detecting a line with just a "." on it. 1093*0Sstevel@tonic-gate * 1094*0Sstevel@tonic-gate * I18n note: It appears that none of the Solaris-supported locales 1095*0Sstevel@tonic-gate * use 0x0a for any purpose other than newline, so looking for '\n' 1096*0Sstevel@tonic-gate * seems safe. 1097*0Sstevel@tonic-gate * All locales use high-bit-set leadin characters for their multi-byte 1098*0Sstevel@tonic-gate * sequences, so a line consisting solely of ".\n" is what it appears 1099*0Sstevel@tonic-gate * to be. 1100*0Sstevel@tonic-gate */ 1101*0Sstevel@tonic-gate static void 1102*0Sstevel@tonic-gate readinitblock(void) 1103*0Sstevel@tonic-gate { 1104*0Sstevel@tonic-gate char buf[100]; 1105*0Sstevel@tonic-gate boolean_t bol; 1106*0Sstevel@tonic-gate 1107*0Sstevel@tonic-gate bol = B_TRUE; 1108*0Sstevel@tonic-gate for (;;) { 1109*0Sstevel@tonic-gate if (fgets(buf, sizeof (buf), stdin) == NULL) 1110*0Sstevel@tonic-gate return; 1111*0Sstevel@tonic-gate if (bol && strcmp(buf, ".\n") == 0) 1112*0Sstevel@tonic-gate return; 1113*0Sstevel@tonic-gate bol = (strchr(buf, '\n') != NULL); 1114*0Sstevel@tonic-gate } 1115*0Sstevel@tonic-gate } 1116*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 1117*0Sstevel@tonic-gate 1118*0Sstevel@tonic-gate /* 1119*0Sstevel@tonic-gate * Report an error, either a fatal one, a warning, or a usage message, 1120*0Sstevel@tonic-gate * depending on the mode parameter. 1121*0Sstevel@tonic-gate */ 1122*0Sstevel@tonic-gate /*ARGSUSED*/ 1123*0Sstevel@tonic-gate static void 1124*0Sstevel@tonic-gate message(enum messagemode mode, char *fmt, ...) 1125*0Sstevel@tonic-gate { 1126*0Sstevel@tonic-gate char *s; 1127*0Sstevel@tonic-gate va_list v; 1128*0Sstevel@tonic-gate 1129*0Sstevel@tonic-gate va_start(v, fmt); 1130*0Sstevel@tonic-gate s = alloc_vsprintf(fmt, v); 1131*0Sstevel@tonic-gate va_end(v); 1132*0Sstevel@tonic-gate 1133*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 1134*0Sstevel@tonic-gate if (embedded) { 1135*0Sstevel@tonic-gate if (mode == WARN) { 1136*0Sstevel@tonic-gate (void) printf("CONV 1\n"); 1137*0Sstevel@tonic-gate (void) printf("PAM_ERROR_MSG\n"); 1138*0Sstevel@tonic-gate } else { /* ERR, USAGE */ 1139*0Sstevel@tonic-gate (void) printf("ERROR\n"); 1140*0Sstevel@tonic-gate } 1141*0Sstevel@tonic-gate if (mode == USAGE) { 1142*0Sstevel@tonic-gate quotemsg("%s", s); 1143*0Sstevel@tonic-gate } else { /* ERR, WARN */ 1144*0Sstevel@tonic-gate quotemsg("%s: %s", myname, s); 1145*0Sstevel@tonic-gate } 1146*0Sstevel@tonic-gate } else { 1147*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 1148*0Sstevel@tonic-gate if (mode == USAGE) { 1149*0Sstevel@tonic-gate (void) fprintf(stderr, "%s\n", s); 1150*0Sstevel@tonic-gate } else { /* ERR, WARN */ 1151*0Sstevel@tonic-gate (void) fprintf(stderr, "%s: %s\n", myname, s); 1152*0Sstevel@tonic-gate } 1153*0Sstevel@tonic-gate #ifdef DYNAMIC_SU 1154*0Sstevel@tonic-gate } 1155*0Sstevel@tonic-gate #endif /* DYNAMIC_SU */ 1156*0Sstevel@tonic-gate 1157*0Sstevel@tonic-gate free(s); 1158*0Sstevel@tonic-gate } 1159*0Sstevel@tonic-gate 1160*0Sstevel@tonic-gate /* 1161*0Sstevel@tonic-gate * Return a pointer to the last path component of a. 1162*0Sstevel@tonic-gate */ 1163*0Sstevel@tonic-gate static char * 1164*0Sstevel@tonic-gate tail(char *a) 1165*0Sstevel@tonic-gate { 1166*0Sstevel@tonic-gate char *p; 1167*0Sstevel@tonic-gate 1168*0Sstevel@tonic-gate p = strrchr(a, '/'); 1169*0Sstevel@tonic-gate if (p == NULL) 1170*0Sstevel@tonic-gate p = a; 1171*0Sstevel@tonic-gate else 1172*0Sstevel@tonic-gate p++; /* step over the '/' */ 1173*0Sstevel@tonic-gate 1174*0Sstevel@tonic-gate return (p); 1175*0Sstevel@tonic-gate } 1176*0Sstevel@tonic-gate 1177*0Sstevel@tonic-gate static char * 1178*0Sstevel@tonic-gate alloc_vsprintf(const char *fmt, va_list ap1) 1179*0Sstevel@tonic-gate { 1180*0Sstevel@tonic-gate va_list ap2; 1181*0Sstevel@tonic-gate int n; 1182*0Sstevel@tonic-gate char buf[1]; 1183*0Sstevel@tonic-gate char *s; 1184*0Sstevel@tonic-gate 1185*0Sstevel@tonic-gate /* 1186*0Sstevel@tonic-gate * We need to scan the argument list twice. Save off a copy 1187*0Sstevel@tonic-gate * of the argument list pointer(s) for the second pass. Note that 1188*0Sstevel@tonic-gate * we are responsible for va_end'ing our copy. 1189*0Sstevel@tonic-gate */ 1190*0Sstevel@tonic-gate va_copy(ap2, ap1); 1191*0Sstevel@tonic-gate 1192*0Sstevel@tonic-gate /* 1193*0Sstevel@tonic-gate * vsnprintf into a dummy to get a length. One might 1194*0Sstevel@tonic-gate * think that passing 0 as the length to snprintf would 1195*0Sstevel@tonic-gate * do what we want, but it's defined not to. 1196*0Sstevel@tonic-gate * 1197*0Sstevel@tonic-gate * Perhaps we should sprintf into a 100 character buffer 1198*0Sstevel@tonic-gate * or something like that, to avoid two calls to snprintf 1199*0Sstevel@tonic-gate * in most cases. 1200*0Sstevel@tonic-gate */ 1201*0Sstevel@tonic-gate n = vsnprintf(buf, sizeof (buf), fmt, ap2); 1202*0Sstevel@tonic-gate va_end(ap2); 1203*0Sstevel@tonic-gate 1204*0Sstevel@tonic-gate /* 1205*0Sstevel@tonic-gate * Allocate an appropriately-sized buffer. 1206*0Sstevel@tonic-gate */ 1207*0Sstevel@tonic-gate s = malloc(n + 1); 1208*0Sstevel@tonic-gate if (s == NULL) { 1209*0Sstevel@tonic-gate perror("malloc"); 1210*0Sstevel@tonic-gate exit(4); 1211*0Sstevel@tonic-gate } 1212*0Sstevel@tonic-gate 1213*0Sstevel@tonic-gate (void) vsnprintf(s, n+1, fmt, ap1); 1214*0Sstevel@tonic-gate 1215*0Sstevel@tonic-gate return (s); 1216*0Sstevel@tonic-gate } 1217