|
Lines 38-43
Link Here
|
| 38 |
restricts who can su to UID 0 accounts. RMS considers that to |
38 |
restricts who can su to UID 0 accounts. RMS considers that to |
| 39 |
be fascist. |
39 |
be fascist. |
| 40 |
|
40 |
|
|
|
41 |
#ifdef USE_PAM |
| 42 |
|
| 43 |
Actually, with PAM, su has nothing to do with whether or not a |
| 44 |
wheel group is enforced by su. RMS tries to restrict your access |
| 45 |
to a su which implements the wheel group, but PAM considers that |
| 46 |
to be fascist, and gives the user/sysadmin the opportunity to |
| 47 |
enforce a wheel group by proper editing of /etc/pam.conf |
| 48 |
|
| 49 |
#endif |
| 50 |
|
| 41 |
Options: |
51 |
Options: |
| 42 |
-, -l, --login Make the subshell a login shell. |
52 |
-, -l, --login Make the subshell a login shell. |
| 43 |
Unset all environment variables except |
53 |
Unset all environment variables except |
|
Lines 81-86
Link Here
|
| 81 |
prototype (returning `int') in <unistd.h>. */ |
91 |
prototype (returning `int') in <unistd.h>. */ |
| 82 |
#define getusershell _getusershell_sys_proto_ |
92 |
#define getusershell _getusershell_sys_proto_ |
| 83 |
|
93 |
|
|
|
94 |
#ifdef USE_PAM |
| 95 |
# include <security/pam_appl.h> |
| 96 |
# include <security/pam_misc.h> |
| 97 |
# include <signal.h> |
| 98 |
# include <sys/wait.h> |
| 99 |
# include <sys/fsuid.h> |
| 100 |
#endif /* USE_PAM */ |
| 101 |
|
| 84 |
#include "system.h" |
102 |
#include "system.h" |
| 85 |
#include "dirname.h" |
103 |
#include "dirname.h" |
| 86 |
|
104 |
|
|
Lines 150-156
Link Here
|
| 150 |
/* The user to become if none is specified. */ |
168 |
/* The user to become if none is specified. */ |
| 151 |
#define DEFAULT_USER "root" |
169 |
#define DEFAULT_USER "root" |
| 152 |
|
170 |
|
|
|
171 |
#ifndef USE_PAM |
| 153 |
char *crypt (); |
172 |
char *crypt (); |
|
|
173 |
#endif |
| 154 |
char *getpass (); |
174 |
char *getpass (); |
| 155 |
char *getusershell (); |
175 |
char *getusershell (); |
| 156 |
void endusershell (); |
176 |
void endusershell (); |
|
Lines 158-165
Link Here
|
| 158 |
|
178 |
|
| 159 |
extern char **environ; |
179 |
extern char **environ; |
| 160 |
|
180 |
|
| 161 |
static void run_shell (const char *, const char *, char **) |
181 |
static void run_shell (const char *, const char *, char **, const struct passwd *) |
|
|
182 |
#ifdef USE_PAM |
| 183 |
; |
| 184 |
#else |
| 162 |
ATTRIBUTE_NORETURN; |
185 |
ATTRIBUTE_NORETURN; |
|
|
186 |
#endif |
| 163 |
|
187 |
|
| 164 |
/* The name this program was run with. */ |
188 |
/* The name this program was run with. */ |
| 165 |
char *program_name; |
189 |
char *program_name; |
|
Lines 271-277
Link Here
|
| 271 |
} |
295 |
} |
| 272 |
#endif |
296 |
#endif |
| 273 |
|
297 |
|
|
|
298 |
#ifdef USE_PAM |
| 299 |
static pam_handle_t *pamh = NULL; |
| 300 |
static int retval; |
| 301 |
static struct pam_conv conv = { |
| 302 |
misc_conv, |
| 303 |
NULL |
| 304 |
}; |
| 305 |
|
| 306 |
#define PAM_BAIL_P if (retval) { \ |
| 307 |
pam_end(pamh, PAM_SUCCESS); \ |
| 308 |
return 0; \ |
| 309 |
} |
| 310 |
#endif |
| 311 |
|
| 274 |
/* Ask the user for a password. |
312 |
/* Ask the user for a password. |
|
|
313 |
If PAM is in use, let PAM ask for the password if necessary. |
| 275 |
Return 1 if the user gives the correct password for entry PW, |
314 |
Return 1 if the user gives the correct password for entry PW, |
| 276 |
0 if not. Return 1 without asking for a password if run by UID 0 |
315 |
0 if not. Return 1 without asking for a password if run by UID 0 |
| 277 |
or if PW has an empty password. */ |
316 |
or if PW has an empty password. */ |
|
Lines 279-284
Link Here
|
| 279 |
static int |
318 |
static int |
| 280 |
correct_password (const struct passwd *pw) |
319 |
correct_password (const struct passwd *pw) |
| 281 |
{ |
320 |
{ |
|
|
321 |
#ifdef USE_PAM |
| 322 |
struct passwd *caller; |
| 323 |
retval = pam_start(PROGRAM_NAME, pw->pw_name, &conv, &pamh); |
| 324 |
PAM_BAIL_P; |
| 325 |
|
| 326 |
if (getuid() != 0 && !isatty(0)) { |
| 327 |
fprintf(stderr, "standard in must be a tty\n"); |
| 328 |
exit(1); |
| 329 |
} |
| 330 |
|
| 331 |
caller = getpwuid(getuid()); |
| 332 |
if(caller != NULL && caller->pw_name != NULL) { |
| 333 |
retval = pam_set_item(pamh, PAM_RUSER, caller->pw_name); |
| 334 |
PAM_BAIL_P; |
| 335 |
} |
| 336 |
|
| 337 |
retval = pam_authenticate(pamh, 0); |
| 338 |
PAM_BAIL_P; |
| 339 |
retval = pam_acct_mgmt(pamh, 0); |
| 340 |
if (retval == PAM_NEW_AUTHTOK_REQD) { |
| 341 |
/* password has expired. Offer option to change it. */ |
| 342 |
retval = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK); |
| 343 |
PAM_BAIL_P; |
| 344 |
} |
| 345 |
PAM_BAIL_P; |
| 346 |
/* must be authenticated if this point was reached */ |
| 347 |
return 1; |
| 348 |
#else /* !USE_PAM */ |
| 282 |
char *unencrypted, *encrypted, *correct; |
349 |
char *unencrypted, *encrypted, *correct; |
| 283 |
#if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP |
350 |
#if HAVE_GETSPNAM && HAVE_STRUCT_SPWD_SP_PWDP |
| 284 |
/* Shadow passwd stuff for SVR3 and maybe other systems. */ |
351 |
/* Shadow passwd stuff for SVR3 and maybe other systems. */ |
|
Lines 303-308
Link Here
|
| 303 |
encrypted = crypt (unencrypted, correct); |
370 |
encrypted = crypt (unencrypted, correct); |
| 304 |
memset (unencrypted, 0, strlen (unencrypted)); |
371 |
memset (unencrypted, 0, strlen (unencrypted)); |
| 305 |
return strcmp (encrypted, correct) == 0; |
372 |
return strcmp (encrypted, correct) == 0; |
|
|
373 |
#endif /* !USE_PAM */ |
| 306 |
} |
374 |
} |
| 307 |
|
375 |
|
| 308 |
/* Update `environ' for the new shell based on PW, with SHELL being |
376 |
/* Update `environ' for the new shell based on PW, with SHELL being |
|
Lines 312-327
Link Here
|
| 312 |
modify_environment (const struct passwd *pw, const char *shell) |
380 |
modify_environment (const struct passwd *pw, const char *shell) |
| 313 |
{ |
381 |
{ |
| 314 |
char *term; |
382 |
char *term; |
|
|
383 |
char *display; |
| 384 |
char *xauthority; |
| 315 |
|
385 |
|
| 316 |
if (simulate_login) |
386 |
if (simulate_login) |
| 317 |
{ |
387 |
{ |
| 318 |
/* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. |
388 |
/* Leave TERM, DISPLAY, XAUTHORITY unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. |
| 319 |
Unset all other environment variables. */ |
389 |
Unset all other environment variables. */ |
| 320 |
term = getenv ("TERM"); |
390 |
term = getenv ("TERM"); |
|
|
391 |
display = getenv ("DISPLAY"); |
| 392 |
xauthority = getenv ("XAUTHORITY"); |
| 321 |
environ = xmalloc (2 * sizeof (char *)); |
393 |
environ = xmalloc (2 * sizeof (char *)); |
| 322 |
environ[0] = 0; |
394 |
environ[0] = 0; |
| 323 |
if (term) |
395 |
if (term) |
| 324 |
xputenv (concat ("TERM", "=", term)); |
396 |
xputenv (concat ("TERM", "=", term)); |
|
|
397 |
if (display) |
| 398 |
xputenv (concat ("DISPLAY", "=", display)); |
| 399 |
if (xauthority) |
| 400 |
xputenv (concat ("XAUTHORITY", "=", xauthority)); |
| 325 |
xputenv (concat ("HOME", "=", pw->pw_dir)); |
401 |
xputenv (concat ("HOME", "=", pw->pw_dir)); |
| 326 |
xputenv (concat ("SHELL", "=", shell)); |
402 |
xputenv (concat ("SHELL", "=", shell)); |
| 327 |
xputenv (concat ("USER", "=", pw->pw_name)); |
403 |
xputenv (concat ("USER", "=", pw->pw_name)); |
|
Lines 358-379
Link Here
|
| 358 |
error (EXIT_FAIL, errno, _("cannot set groups")); |
434 |
error (EXIT_FAIL, errno, _("cannot set groups")); |
| 359 |
endgrent (); |
435 |
endgrent (); |
| 360 |
#endif |
436 |
#endif |
|
|
437 |
#ifdef USE_PAM |
| 438 |
retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); |
| 439 |
if (retval != PAM_SUCCESS) |
| 440 |
error (1, 0, pam_strerror(pamh, retval)); |
| 441 |
#endif /* USE_PAM */ |
| 361 |
if (setgid (pw->pw_gid)) |
442 |
if (setgid (pw->pw_gid)) |
| 362 |
error (EXIT_FAIL, errno, _("cannot set group id")); |
443 |
error (EXIT_FAIL, errno, _("cannot set group id")); |
| 363 |
if (setuid (pw->pw_uid)) |
444 |
if (setuid (pw->pw_uid)) |
| 364 |
error (EXIT_FAIL, errno, _("cannot set user id")); |
445 |
error (EXIT_FAIL, errno, _("cannot set user id")); |
| 365 |
} |
446 |
} |
| 366 |
|
447 |
|
|
|
448 |
#ifdef USE_PAM |
| 449 |
static int caught=0; |
| 450 |
/* Signal handler for parent process later */ |
| 451 |
static void su_catch_sig(int sig) |
| 452 |
{ |
| 453 |
++caught; |
| 454 |
} |
| 455 |
|
| 456 |
int |
| 457 |
pam_copyenv (pam_handle_t *pamh) |
| 458 |
{ |
| 459 |
char **env; |
| 460 |
|
| 461 |
env = pam_getenvlist(pamh); |
| 462 |
if(env) { |
| 463 |
while(*env) { |
| 464 |
xputenv(*env); |
| 465 |
env++; |
| 466 |
} |
| 467 |
} |
| 468 |
return(0); |
| 469 |
} |
| 470 |
#endif |
| 471 |
|
| 367 |
/* Run SHELL, or DEFAULT_SHELL if SHELL is empty. |
472 |
/* Run SHELL, or DEFAULT_SHELL if SHELL is empty. |
| 368 |
If COMMAND is nonzero, pass it to the shell with the -c option. |
473 |
If COMMAND is nonzero, pass it to the shell with the -c option. |
| 369 |
If ADDITIONAL_ARGS is nonzero, pass it to the shell as more |
474 |
If ADDITIONAL_ARGS is nonzero, pass it to the shell as more |
| 370 |
arguments. */ |
475 |
arguments. */ |
| 371 |
|
476 |
|
| 372 |
static void |
477 |
static void |
| 373 |
run_shell (const char *shell, const char *command, char **additional_args) |
478 |
run_shell (const char *shell, const char *command, char **additional_args, const struct passwd *pw) |
| 374 |
{ |
479 |
{ |
| 375 |
const char **args; |
480 |
const char **args; |
| 376 |
int argno = 1; |
481 |
int argno = 1; |
|
|
482 |
#ifdef USE_PAM |
| 483 |
int child; |
| 484 |
sigset_t ourset; |
| 485 |
int status; |
| 486 |
|
| 487 |
retval = pam_open_session(pamh,0); |
| 488 |
if (retval != PAM_SUCCESS) { |
| 489 |
fprintf (stderr, "could not open session\n"); |
| 490 |
exit (1); |
| 491 |
} |
| 492 |
|
| 493 |
/* do this at the last possible moment, because environment variables may |
| 494 |
be passed even in the session phase |
| 495 |
*/ |
| 496 |
if(pam_copyenv(pamh) != PAM_SUCCESS) |
| 497 |
fprintf (stderr, "error copying PAM environment\n"); |
| 498 |
|
| 499 |
child = fork(); |
| 500 |
if (child == 0) { /* child shell */ |
| 501 |
change_identity (pw); |
| 502 |
pam_end(pamh, 0); |
| 503 |
#endif |
| 377 |
|
504 |
|
| 378 |
if (additional_args) |
505 |
if (additional_args) |
| 379 |
args = xmalloc (sizeof (char *) |
506 |
args = xmalloc (sizeof (char *) |
|
Lines 385-390
Link Here
|
| 385 |
char *arg0; |
512 |
char *arg0; |
| 386 |
char *shell_basename; |
513 |
char *shell_basename; |
| 387 |
|
514 |
|
|
|
515 |
if(chdir(pw->pw_dir)) |
| 516 |
error(0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); |
| 517 |
|
| 388 |
shell_basename = base_name (shell); |
518 |
shell_basename = base_name (shell); |
| 389 |
arg0 = xmalloc (strlen (shell_basename) + 2); |
519 |
arg0 = xmalloc (strlen (shell_basename) + 2); |
| 390 |
arg0[0] = '-'; |
520 |
arg0[0] = '-'; |
|
Lines 411-416
Link Here
|
| 411 |
error (0, errno, "%s", shell); |
541 |
error (0, errno, "%s", shell); |
| 412 |
exit (exit_status); |
542 |
exit (exit_status); |
| 413 |
} |
543 |
} |
|
|
544 |
#ifdef USE_PAM |
| 545 |
} else if (child == -1) { |
| 546 |
fprintf(stderr, "can not fork user shell: %s", strerror(errno)); |
| 547 |
exit(1); |
| 548 |
} |
| 549 |
/* parent only */ |
| 550 |
sigfillset(&ourset); |
| 551 |
if (sigprocmask(SIG_BLOCK, &ourset, NULL)) { |
| 552 |
fprintf(stderr, "%s: signal malfunction\n", PROGRAM_NAME); |
| 553 |
caught = 1; |
| 554 |
} |
| 555 |
if (!caught) { |
| 556 |
struct sigaction action; |
| 557 |
action.sa_handler = su_catch_sig; |
| 558 |
sigemptyset(&action.sa_mask); |
| 559 |
action.sa_flags = 0; |
| 560 |
sigemptyset(&ourset); |
| 561 |
if (sigaddset(&ourset, SIGTERM) |
| 562 |
|| sigaddset(&ourset, SIGALRM) |
| 563 |
|| sigaction(SIGTERM, &action, NULL) |
| 564 |
|| sigprocmask(SIG_UNBLOCK, &ourset, NULL)) { |
| 565 |
fprintf(stderr, "%s: signal masking malfunction\n", PROGRAM_NAME); |
| 566 |
caught = 1; |
| 567 |
} |
| 568 |
} |
| 569 |
if (!caught) { |
| 570 |
do { |
| 571 |
int pid; |
| 572 |
|
| 573 |
pid = waitpid(-1, &status, WUNTRACED); |
| 574 |
|
| 575 |
if (WIFSTOPPED(status)) { |
| 576 |
kill(getpid(), SIGSTOP); |
| 577 |
/* once we get here, we must have resumed */ |
| 578 |
kill(pid, SIGCONT); |
| 579 |
} |
| 580 |
} while (WIFSTOPPED(status)); |
| 581 |
} |
| 582 |
|
| 583 |
if (caught) { |
| 584 |
fprintf(stderr, "\nSession terminated, killing shell..."); |
| 585 |
kill (child, SIGTERM); |
| 586 |
} |
| 587 |
retval = pam_close_session(pamh, 0); |
| 588 |
PAM_BAIL_P; |
| 589 |
retval = pam_end(pamh, PAM_SUCCESS); |
| 590 |
PAM_BAIL_P; |
| 591 |
if (caught) { |
| 592 |
sleep(2); |
| 593 |
kill(child, SIGKILL); |
| 594 |
fprintf(stderr, " ...killed.\n"); |
| 595 |
exit(-1); |
| 596 |
} |
| 597 |
exit (WEXITSTATUS(status)); |
| 598 |
#endif /* USE_PAM */ |
| 414 |
} |
599 |
} |
| 415 |
|
600 |
|
| 416 |
/* Return 1 if SHELL is a restricted shell (one not returned by |
601 |
/* Return 1 if SHELL is a restricted shell (one not returned by |
|
Lines 588-594
Link Here
|
| 588 |
|
773 |
|
|
|
774 |
|
| 775 |
#ifndef USE_PAM |
| 589 |
change_identity (pw); |
776 |
change_identity (pw); |
| 590 |
if (simulate_login && chdir (pw->pw_dir)) |
777 |
#endif |
| 591 |
error (0, errno, _("warning: cannot change directory to %s"), pw->pw_dir); |
|
|
| 592 |
|
778 |
|
| 593 |
run_shell (shell, command, additional_args); |
779 |
run_shell (shell, command, additional_args, pw); |
| 594 |
} |
780 |
} |