]>
git.saurik.com Git - apple/shell_cmds.git/blob - su/su.c
750d02b3f941925aa31dfb37cecb18e5417db544
1 /* $NetBSD: su.c,v 1.26 1998/08/25 20:59:40 ross Exp $ */
4 * Copyright (c) 1988 The Regents of the University of California.
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 * 3. All advertising materials mentioning features or use of this software
16 * must display the following acknowledgement:
17 * This product includes software developed by the University of
18 * California, Berkeley and its contributors.
19 * 4. Neither the name of the University nor the names of its contributors
20 * may be used to endorse or promote products derived from this software
21 * without specific prior written permission.
23 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
24 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
25 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
26 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
27 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
28 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
29 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
30 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
32 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 #include <sys/cdefs.h>
39 "@(#) Copyright (c) 1988 The Regents of the University of California.\n\
40 All rights reserved.\n");
45 static char sccsid
[] = "@(#)su.c 8.3 (Berkeley) 4/2/94";*/
47 __RCSID("$NetBSD: su.c,v 1.26 1998/08/25 20:59:40 ross Exp $");
51 #include <sys/param.h>
53 #include <sys/resource.h>
71 #include <kerberosIV/des.h>
72 #include <kerberosIV/krb.h>
75 #define ARGSTR "-Kflm"
79 static int kerberos
__P((char *, char *, int));
80 static int koktologin
__P((char *, char *, char *));
87 #define SUGROUP "wheel"
91 int main
__P((int, char **));
93 static int chshell
__P((const char *));
94 static char *ontty
__P((void));
102 extern char *__progname
;
103 extern char **environ
;
109 int asme
, ch
, asthem
, fastlogin
, prio
;
110 enum { UNSET
, YES
, NO
} iscsh
= UNSET
;
111 char *user
, *shell
, *avshell
, *username
, *cleanenv
[10], **np
;
112 char shellbuf
[MAXPATHLEN
], avshellbuf
[MAXPATHLEN
];
114 asme
= asthem
= fastlogin
= 0;
116 while ((ch
= getopt(argc
, argv
, ARGSTR
)) != -1)
137 (void)fprintf(stderr
,
138 "Usage: %s [%s] [login [shell arguments]]\n",
145 prio
= getpriority(PRIO_PROCESS
, 0);
148 (void)setpriority(PRIO_PROCESS
, 0, -2);
149 openlog("su", LOG_CONS
, 0);
151 /* get current login name and shell */
153 username
= getlogin();
154 if (username
== NULL
|| (pwd
= getpwnam(username
)) == NULL
||
156 pwd
= getpwuid(ruid
);
158 errx(1, "who are you?");
159 username
= strdup(pwd
->pw_name
);
160 usergid
= pwd
->pw_gid
;
161 if (username
== NULL
)
165 if (pwd
->pw_shell
&& *pwd
->pw_shell
) {
166 shell
= strncpy(shellbuf
, pwd
->pw_shell
,
167 sizeof(shellbuf
) - 1);
168 shellbuf
[sizeof(shellbuf
) - 1] = '\0';
170 shell
= _PATH_BSHELL
;
174 /* get target login information, default to root */
175 user
= *argv
? *argv
: "root";
176 np
= *argv
? argv
: argv
-1;
178 if ((pwd
= getpwnam(user
)) == NULL
)
179 errx(1, "unknown login %s", user
);
183 && (!use_kerberos
|| kerberos(username
, user
, pwd
->pw_uid
))
186 gid_t groups
[NGROUPS_MAX
];
190 * Only allow those in group SUGROUP to su to root.
192 if (pwd
->pw_uid
== 0 &&
193 (initgroups(username
, usergid
) == 0) &&
194 ((numgroups
= getgroups(NGROUPS_MAX
, groups
)) > 0)) {
198 struct group
*wheelgroup
;
200 if ((wheelgroup
= getgrnam(SUGROUP
)) != NULL
) {
201 wheelgid
= wheelgroup
->gr_gid
;
203 for (i
= 0; i
< numgroups
; i
++) {
204 if (groups
[i
] == wheelgid
) {
210 /* If we can't get the gid for SUGROUP, then let anyone su */
211 warnx("unknown secondary group %s; anyone can su %s.", SUGROUP
, user
);
217 "you are not listed in the correct secondary group (%s) to su %s.",
221 /* if target requires a password, verify it */
222 if (*pwd
->pw_passwd
) {
223 p
= getpass("Password:");
225 if (strcasecmp(p
, "s/key") == 0) {
226 if (skey_haskey(user
))
227 errx(1, "Sorry, you have no s/key.");
229 if (skey_authenticate(user
)) {
236 if (strcmp(pwd
->pw_passwd
, crypt(p
, pwd
->pw_passwd
))) {
240 fprintf(stderr
, "Sorry\n");
241 syslog(LOG_AUTH
|LOG_WARNING
,
242 "BAD SU %s to %s%s", username
,
250 /* if asme and non-standard target shell, must be root */
251 if (!chshell(pwd
->pw_shell
) && ruid
)
252 errx(1,"permission denied (shell).");
253 } else if (pwd
->pw_shell
&& *pwd
->pw_shell
) {
254 shell
= pwd
->pw_shell
;
257 shell
= _PATH_BSHELL
;
261 if ((p
= strrchr(shell
, '/')) != NULL
)
266 /* if we're forking a csh, we want to slightly muck the args */
268 iscsh
= strstr(avshell
, "csh") ? YES
: NO
;
270 /* set permissions */
271 if (setgid(pwd
->pw_gid
) < 0)
273 if (initgroups(user
, pwd
->pw_gid
))
274 errx(1, "initgroups failed");
275 if (setuid(pwd
->pw_uid
) < 0)
283 (void)setenv("PATH", _PATH_DEFPATH
, 1);
285 (void)setenv("TERM", p
, 1);
286 if (chdir(pwd
->pw_dir
) < 0)
287 errx(1, "no directory");
289 if (asthem
|| pwd
->pw_uid
)
290 (void)setenv("USER", pwd
->pw_name
, 1);
291 (void)setenv("HOME", pwd
->pw_dir
, 1);
292 (void)setenv("SHELL", shell
, 1);
304 (void)strncpy(avshellbuf
+1, avshell
, sizeof(avshellbuf
) - 2);
305 avshell
= avshellbuf
;
306 } else if (iscsh
== YES
) {
307 /* csh strips the first character... */
309 (void)strncpy(avshellbuf
+1, avshell
, sizeof(avshellbuf
) - 2);
310 avshell
= avshellbuf
;
314 if (pwd
->pw_change
|| pwd
->pw_expire
)
315 (void)gettimeofday(&tp
, (struct timezone
*)NULL
);
316 if (pwd
->pw_change
) {
317 if (tp
.tv_sec
>= pwd
->pw_change
) {
318 (void)printf("%s -- %s's password has expired.\n",
319 (ruid
? "Sorry" : "Note"), user
);
322 } else if (pwd
->pw_change
- tp
.tv_sec
<
323 _PASSWORD_WARNDAYS
* SECSPERDAY
)
324 (void)printf("Warning: %s's password expires on %s",
325 user
, ctime(&pwd
->pw_change
));
327 if (pwd
->pw_expire
) {
328 if (tp
.tv_sec
>= pwd
->pw_expire
) {
329 (void)printf("%s -- %s's account has expired.\n",
330 (ruid
? "Sorry" : "Note"), user
);
333 } else if (pwd
->pw_expire
- tp
.tv_sec
<
334 _PASSWORD_WARNDAYS
* SECSPERDAY
)
335 (void)printf("Warning: %s's account expires on %s",
336 user
, ctime(&pwd
->pw_expire
));
339 syslog(LOG_NOTICE
|LOG_AUTH
, "%s to %s%s",
340 username
, user
, ontty());
342 (void)setpriority(PRIO_PROCESS
, 0, prio
);
355 while ((cp
= getusershell()) != NULL
)
365 static char buf
[MAXPATHLEN
+ 4];
368 if ((p
= ttyname(STDERR_FILENO
)) != NULL
)
369 (void)snprintf(buf
, sizeof buf
, " on %s", p
);
375 kerberos(username
, user
, uid
)
376 char *username
, *user
;
384 char lrealm
[REALM_SZ
], krbtkfile
[MAXPATHLEN
];
385 char hostname
[MAXHOSTNAMELEN
+ 1], savehost
[MAXHOSTNAMELEN
+ 1];
387 if (krb_get_lrealm(lrealm
, 1) != KSUCCESS
)
389 if (koktologin(username
, lrealm
, user
) && !uid
) {
390 warnx("kerberos: not in %s's ACL.", user
);
393 (void)(void)snprintf(krbtkfile
, sizeof krbtkfile
, "%s_%s_%d", TKT_ROOT
,
396 (void)setenv("KRBTKFILE", krbtkfile
, 1);
397 (void)krb_set_tkt_string(krbtkfile
);
399 * Set real as well as effective ID to 0 for the moment,
400 * to make the kerberos library do the right thing.
408 * Little trick here -- if we are su'ing to root,
409 * we need to get a ticket for "xxx.root", where xxx represents
410 * the name of the person su'ing. Otherwise (non-root case),
411 * we need to get a ticket for "yyy.", where yyy represents
412 * the name of the person being su'd to, and the instance is null
414 * We should have a way to set the ticket lifetime,
415 * with a system default for root.
417 kerno
= krb_get_pw_in_tkt((uid
== 0 ? username
: user
),
418 (uid
== 0 ? "root" : ""), lrealm
,
419 "krbtgt", lrealm
, DEFAULT_TKT_LIFE
, 0);
421 if (kerno
!= KSUCCESS
) {
422 if (kerno
== KDC_PR_UNKNOWN
) {
423 warnx("kerberos: principal unknown: %s.%s@%s",
424 (uid
== 0 ? username
: user
),
425 (uid
== 0 ? "root" : ""), lrealm
);
428 warnx("kerberos: unable to su: %s", krb_err_txt
[kerno
]);
429 syslog(LOG_NOTICE
|LOG_AUTH
,
430 "BAD Kerberos SU: %s to %s%s: %s",
431 username
, user
, ontty(), krb_err_txt
[kerno
]);
435 if (chown(krbtkfile
, uid
, -1) < 0) {
437 (void)unlink(krbtkfile
);
441 (void)setpriority(PRIO_PROCESS
, 0, -2);
443 if (gethostname(hostname
, sizeof(hostname
)) == -1) {
448 hostname
[sizeof(hostname
) - 1] = '\0';
450 (void)strncpy(savehost
, krb_get_phost(hostname
), sizeof(savehost
));
451 savehost
[sizeof(savehost
) - 1] = '\0';
453 kerno
= krb_mk_req(&ticket
, "rcmd", savehost
, lrealm
, 33);
455 if (kerno
== KDC_PR_UNKNOWN
) {
456 warnx("Warning: TGT not verified.");
457 syslog(LOG_NOTICE
|LOG_AUTH
,
458 "%s to %s%s, TGT not verified (%s); %s.%s not registered?",
459 username
, user
, ontty(), krb_err_txt
[kerno
],
461 } else if (kerno
!= KSUCCESS
) {
462 warnx("Unable to use TGT: %s", krb_err_txt
[kerno
]);
463 syslog(LOG_NOTICE
|LOG_AUTH
, "failed su: %s to %s%s: %s",
464 username
, user
, ontty(), krb_err_txt
[kerno
]);
468 if (!(hp
= gethostbyname(hostname
))) {
469 warnx("can't get addr of %s", hostname
);
473 memmove((char *)&faddr
, (char *)hp
->h_addr
, sizeof(faddr
));
475 if ((kerno
= krb_rd_req(&ticket
, "rcmd", savehost
, faddr
,
476 &authdata
, "")) != KSUCCESS
) {
477 warnx("kerberos: unable to verify rcmd ticket: %s\n",
479 syslog(LOG_NOTICE
|LOG_AUTH
,
480 "failed su: %s to %s%s: %s", username
,
481 user
, ontty(), krb_err_txt
[kerno
]);
490 koktologin(name
, realm
, toname
)
491 char *name
, *realm
, *toname
;
497 memset((char *)kdata
, 0, sizeof(*kdata
));
498 (void)strncpy(kdata
->pname
, name
, sizeof(kdata
->pname
) - 1);
499 (void)strncpy(kdata
->pinst
,
500 ((strcmp(toname
, "root") == 0) ? "root" : ""), sizeof(kdata
->pinst
) - 1);
501 (void)strncpy(kdata
->prealm
, realm
, sizeof(kdata
->prealm
) - 1);
502 return (kuserok(kdata
, toname
));