* Copyright (c) 1988 The Regents of the University of California.
* %sccs.include.redist.c%
"@(#) Copyright (c) 1988 The Regents of the University of California.\n\
static char sccsid
[] = "@(#)su.c 5.25 (Berkeley) %G%";
#include <sys/resource.h>
#include <kerberosIV/des.h>
#include <kerberosIV/krb.h>
extern int errno
, optind
;
register struct passwd
*pwd
;
int asme
, ch
, asthem
, fastlogin
, prio
;
enum { UNSET
, YES
, NO
} iscsh
= UNSET
;
char *user
, *shell
, *username
, *cleanenv
[2], *nargv
[4], **np
;
char shellbuf
[MAXPATHLEN
];
char *crypt(), *getpass(), *getenv(), *getlogin(), *ontty();
asme
= asthem
= fastlogin
= 0;
while ((ch
= getopt(argc
, argv
, ARGSTR
)) != EOF
)
(void)fprintf(stderr
, "usage: su [%s] [login]\n",
prio
= getpriority(PRIO_PROCESS
, 0);
(void)setpriority(PRIO_PROCESS
, 0, -2);
openlog("su", LOG_CONS
, 0);
/* get current login name and shell */
if (username
== NULL
|| (pwd
= getpwnam(username
)) == NULL
||
fprintf(stderr
, "su: who are you?\n");
username
= strdup(pwd
->pw_name
);
if (pwd
->pw_shell
&& *pwd
->pw_shell
)
shell
= strcpy(shellbuf
, pwd
->pw_shell
);
/* get target login information, default to root */
user
= *argv
? *argv
: "root";
if ((pwd
= getpwnam(user
)) == NULL
) {
fprintf(stderr
, "su: unknown login %s\n", user
);
/* only allow those in group zero to su to root. */
if (pwd
->pw_uid
== 0 && (gr
= getgrgid((gid_t
)0)))
for (g
= gr
->gr_mem
;; ++g
) {
"su: you are not in the correct group to su %s.\n", user
);
if (!strcmp(username
, *g
))
if (!use_kerberos
|| kerberos(username
, user
, pwd
->pw_uid
))
/* if target requires a password, verify it */
p
= getpass("Password:");
if (strcmp(pwd
->pw_passwd
, crypt(p
, pwd
->pw_passwd
))) {
fprintf(stderr
, "Sorry\n");
syslog(LOG_AUTH
|LOG_WARNING
,
"BAD SU %s to %s%s", username
,
/* if asme and non-standard target shell, must be root */
if (!chshell(pwd
->pw_shell
) && ruid
) {
"su: permission denied (shell).\n");
} else if (pwd
->pw_shell
&& *pwd
->pw_shell
) {
/* if we're forking a csh, we want to slightly muck the args */
if (p
= rindex(shell
, '/'))
iscsh
= strcmp(p
, "csh") ? NO
: YES
;
if (setgid(pwd
->pw_gid
) < 0) {
if (initgroups(user
, pwd
->pw_gid
)) {
(void)fprintf(stderr
, "su: initgroups failed.\n");
if (setuid(pwd
->pw_uid
) < 0) {
cleanenv
[0] = _PATH_DEFPATH
;
(void)setenv("TERM", p
, 1);
if (chdir(pwd
->pw_dir
) < 0) {
fprintf(stderr
, "su: no directory\n");
if (asthem
|| pwd
->pw_uid
)
(void)setenv("USER", pwd
->pw_name
, 1);
(void)setenv("HOME", pwd
->pw_dir
, 1);
(void)setenv("SHELL", shell
, 1);
/* csh strips the first character... */
*np
= asthem
? "-su" : iscsh
== YES
? "_su" : "su";
syslog(LOG_NOTICE
|LOG_AUTH
, "%s to %s%s",
username
, user
, ontty());
(void)setpriority(PRIO_PROCESS
, 0, prio
);
(void)fprintf(stderr
, "su: %s not found.\n", shell
);
while ((cp
= getusershell()) != NULL
)
static char buf
[MAXPATHLEN
+ 4];
if (p
= ttyname(STDERR_FILENO
))
sprintf(buf
, " on %s", p
);
kerberos(username
, user
, uid
)
extern char *krb_err_txt
[];
char lrealm
[REALM_SZ
], krbtkfile
[MAXPATHLEN
];
char hostname
[MAXHOSTNAMELEN
], savehost
[MAXHOSTNAMELEN
];
char *ontty(), *krb_get_phost();
if (krb_get_lrealm(lrealm
, 1) != KSUCCESS
) {
(void)fprintf(stderr
, "su: couldn't get local realm.\n");
if (koktologin(username
, lrealm
, user
) && !uid
) {
(void)fprintf(stderr
, "kerberos su: not in %s's ACL.\n", user
);
(void)sprintf(krbtkfile
, "%s_%s_%d", TKT_ROOT
, user
, getuid());
(void)setenv("KRBTKFILE", krbtkfile
, 1);
* Set real as well as effective ID to 0 for the moment,
* to make the kerberos library do the right thing.
* Little trick here -- if we are su'ing to root,
* we need to get a ticket for "xxx.root", where xxx represents
* the name of the person su'ing. Otherwise (non-root case),
* we need to get a ticket for "yyy.", where yyy represents
* the name of the person being su'd to, and the instance is null
* We should have a way to set the ticket lifetime,
* with a system default for root.
kerno
= krb_get_pw_in_tkt((uid
== 0 ? username
: user
),
(uid
== 0 ? "root" : ""), lrealm
,
"krbtgt", lrealm
, DEFAULT_TKT_LIFE
, 0);
if (kerno
== KDC_PR_UNKNOWN
) {
fprintf(stderr
, "principal unknown: %s.%s@%s\n",
(uid
== 0 ? username
: user
),
(uid
== 0 ? "root" : ""), lrealm
);
(void)printf("su: unable to su: %s\n", krb_err_txt
[kerno
]);
syslog(LOG_NOTICE
|LOG_AUTH
,
"su: BAD Kerberos SU: %s to %s%s: %s",
username
, user
, ontty(), krb_err_txt
[kerno
]);
if (chown(krbtkfile
, uid
, -1) < 0) {
(void)setpriority(PRIO_PROCESS
, 0, -2);
if (gethostname(hostname
, sizeof(hostname
)) == -1) {
(void)strncpy(savehost
, krb_get_phost(hostname
), sizeof(savehost
));
savehost
[sizeof(savehost
) - 1] = '\0';
kerno
= krb_mk_req(&ticket
, "rcmd", savehost
, lrealm
, 33);
if (kerno
== KDC_PR_UNKNOWN
) {
(void)printf("Warning: tgt not verified.\n");
syslog(LOG_NOTICE
|LOG_AUTH
,
"su: %s to %s%s, TGT not verified",
username
, user
, ontty());
} else if (kerno
!= KSUCCESS
) {
(void)printf("Unable to use TGT: %s\n", krb_err_txt
[kerno
]);
syslog(LOG_NOTICE
|LOG_AUTH
, "su: failed su: %s to %s%s: %s",
username
, user
, ontty(), krb_err_txt
[kerno
]);
if (!(hp
= gethostbyname(hostname
))) {
(void)printf("su: can't get addr of %s\n", hostname
);
(void)bcopy((char *)hp
->h_addr
, (char *)&faddr
, sizeof(faddr
));
if ((kerno
= krb_rd_req(&ticket
, "rcmd", savehost
, faddr
,
&authdata
, "")) != KSUCCESS
) {
(void)printf("su: unable to verify rcmd ticket: %s\n",
syslog(LOG_NOTICE
|LOG_AUTH
,
"su: failed su: %s to %s%s: %s", username
,
ontty(), user
, krb_err_txt
[kerno
]);
koktologin(name
, realm
, toname
)
char *name
, *realm
, *toname
;
register AUTH_DAT
*kdata
;
bzero((caddr_t
) kdata
, sizeof(*kdata
));
(void)strcpy(kdata
->pname
, name
);
(void)strcpy(kdata
->pinst
,
((strcmp(toname
, "root") == 0) ? "root" : ""));
(void)strcpy(kdata
->prealm
, realm
);
return(kuserok(kdata
, toname
));