1 Modify ksu so that it performs account and session management on behalf of
2 the target user account, mimicking the action of regular su. The default
3 service name is "ksu", because on Fedora at least the configuration used
4 is determined by whether or not a login shell is being opened, and so
5 this may need to vary, too. At run-time, ksu's behavior can be reset to
6 the earlier, non-PAM behavior by setting "use_pam" to false in the [ksu]
7 section of /etc/krb5.conf.
9 When enabled, ksu gains a dependency on libpam.
11 Originally RT#5939, though it's changed since then to perform the account
12 and session management before dropping privileges.
14 diff -up krb5-1.8/src/aclocal.m4.pam krb5-1.8/src/aclocal.m4
15 --- krb5-1.8/src/aclocal.m4.pam 2009-11-22 12:00:45.000000000 -0500
16 +++ krb5-1.8/src/aclocal.m4 2010-03-05 10:48:08.000000000 -0500
17 @@ -1703,3 +1703,70 @@ AC_DEFUN(KRB5_AC_KEYRING_CCACHE,[
22 +dnl Use PAM instead of local crypt() compare for checking local passwords,
23 +dnl and perform PAM account, session management, and password-changing where
26 +AC_DEFUN(KRB5_WITH_PAM,[
27 +AC_ARG_WITH(pam,[AC_HELP_STRING(--with-pam,[compile with PAM support])],
28 + withpam="$withval",withpam=auto)
29 +AC_ARG_WITH(pam-ksu-service,[AC_HELP_STRING(--with-ksu-service,[PAM service name for ksu ["ksu"]])],
30 + withksupamservice="$withval",withksupamservice=ksu)
32 +if test "$withpam" != no ; then
33 + AC_MSG_RESULT([checking for PAM...])
36 + AC_CHECK_HEADERS(security/pam_appl.h)
37 + if test "x$ac_cv_header_security_pam_appl_h" != xyes ; then
38 + if test "$withpam" = auto ; then
39 + AC_MSG_RESULT([Unable to locate security/pam_appl.h.])
42 + AC_MSG_ERROR([Unable to locate security/pam_appl.h.])
47 + unset ac_cv_func_pam_start
48 + AC_CHECK_FUNCS(putenv pam_start)
49 + if test "x$ac_cv_func_pam_start" = xno ; then
50 + unset ac_cv_func_pam_start
51 + AC_CHECK_LIB(dl,dlopen)
52 + AC_CHECK_FUNCS(pam_start)
53 + if test "x$ac_cv_func_pam_start" = xno ; then
54 + AC_CHECK_LIB(pam,pam_start)
55 + unset ac_cv_func_pam_start
56 + unset ac_cv_func_pam_getenvlist
57 + AC_CHECK_FUNCS(pam_start pam_getenvlist)
58 + if test "x$ac_cv_func_pam_start" = xyes ; then
61 + if test "$withpam" = auto ; then
62 + AC_MSG_RESULT([Unable to locate libpam.])
65 + AC_MSG_ERROR([Unable to locate libpam.])
70 + if test "$withpam" != no ; then
71 + AC_MSG_NOTICE([building with PAM support])
72 + AC_DEFINE(USE_PAM,1,[Define if Kerberos-aware tools should support PAM])
73 + AC_DEFINE_UNQUOTED(KSU_PAM_SERVICE,"$withksupamservice",
74 + [Define to the name of the PAM service name to be used by ksu.])
76 + NON_PAM_MAN=".\\\" "
86 +AC_SUBST(NON_PAM_MAN)
88 diff -up krb5-1.8/src/clients/ksu/main.c.pam krb5-1.8/src/clients/ksu/main.c
89 --- krb5-1.8/src/clients/ksu/main.c.pam 2009-11-02 22:27:56.000000000 -0500
90 +++ krb5-1.8/src/clients/ksu/main.c 2010-03-05 10:48:08.000000000 -0500
92 * KSU was writen by: Ari Medvinsky, ari@isi.edu
95 +#include "autoconf.h"
97 #include "adm_proto.h"
98 #include <sys/types.h>
110 @@ -40,6 +45,7 @@ char k5login_path[MAXPATHLEN];
111 char k5users_path[MAXPATHLEN];
112 char * gb_err = NULL;
117 #define _DEF_CSH "/bin/csh"
118 @@ -586,6 +592,25 @@ main (argc, argv)
119 prog_name,target_user,client_name,
120 source_user,ontty());
123 + if (appl_pam_enabled(ksu_context, "ksu")) {
124 + if (appl_pam_acct_mgmt(KSU_PAM_SERVICE, 1, target_user, NULL,
126 + ttyname(STDERR_FILENO)) != 0) {
127 + fprintf(stderr, "Access denied for %s.\n", target_user);
128 + sweep_up(ksu_context, cc_target);
131 + if (appl_pam_requires_chauthtok()) {
132 + fprintf(stderr, "Password change required for %s.\n",
134 + sweep_up(ksu_context, cc_target);
141 /* Run authorization as target.*/
142 if (krb5_seteuid(target_uid)) {
143 com_err(prog_name, errno, _("while switching to target for "
145 sweep_up(ksu_context, cc_target);
150 + /* we always do PAM account management, even for root */
151 + if (appl_pam_enabled(ksu_context, "ksu")) {
152 + if (appl_pam_acct_mgmt(KSU_PAM_SERVICE, 1, target_user, NULL,
154 + ttyname(STDERR_FILENO)) != 0) {
155 + fprintf(stderr, "Access denied for %s.\n", target_user);
156 + sweep_up(ksu_context, cc_target);
159 + if (appl_pam_requires_chauthtok()) {
160 + fprintf(stderr, "Password change required for %s.\n",
162 + sweep_up(ksu_context, cc_target);
176 + if (appl_pam_enabled(ksu_context, "ksu")) {
177 + if (appl_pam_session_open() != 0) {
178 + fprintf(stderr, "Error opening session for %s.\n", target_user);
179 + sweep_up(ksu_context, cc_target);
184 + printf(" Opened PAM session.\n");
187 + if (appl_pam_cred_init()) {
188 + fprintf(stderr, "Error initializing credentials for %s.\n",
190 + sweep_up(ksu_context, cc_target);
195 + printf(" Initialized PAM credentials.\n");
201 /* set permissions */
202 if (setgid(target_pwd->pw_gid) < 0) {
203 perror("ksu: setgid");
204 @@ -792,7 +817,7 @@ main (argc, argv)
205 fprintf(stderr, "program to be execed %s\n",params[0]);
208 - if( keep_target_cache ) {
209 + if( keep_target_cache && !force_fork ) {
210 execv(params[0], params);
211 com_err(prog_name, errno, _("while trying to execv %s"), params[0]);
212 sweep_up(ksu_context, cc_target);
213 @@ -823,16 +875,35 @@ main (argc, argv)
215 com_err(prog_name, errno, _("while calling waitpid"));
217 - sweep_up(ksu_context, cc_target);
218 + if( !keep_target_cache ) {
219 + sweep_up(ksu_context, cc_target);
223 com_err(prog_name, errno, _("while trying to fork."));
224 sweep_up(ksu_context, cc_target);
228 + if (appl_pam_enabled(ksu_context, "ksu")) {
229 + if (appl_pam_setenv() != 0) {
230 + fprintf(stderr, "Error setting up environment for %s.\n",
236 + printf(" Set up PAM environment.\n");
241 execv(params[0], params);
242 com_err(prog_name, errno, _("while trying to execv %s"),
244 + if( keep_target_cache ) {
245 + sweep_up(ksu_context, cc_target);
250 diff -up krb5-1.8/src/clients/ksu/Makefile.in.pam krb5-1.8/src/clients/ksu/Makefile.in
251 --- krb5-1.8/src/clients/ksu/Makefile.in.pam 2009-11-22 13:13:29.000000000 -0500
252 +++ krb5-1.8/src/clients/ksu/Makefile.in 2010-03-05 11:55:14.000000000 -0500
254 PROG_RPATH=$(KRB5_LIBDIR)
260 $(srcdir)/krb_auth_su.c \
262 $(srcdir)/authorization.c \
265 $(srcdir)/heuristic.c \
266 $(srcdir)/xmalloc.c \
268 @@ -21,13 +23,17 @@ OBJS = \
274 xmalloc.o @SETENVOBJ@
278 ksu: $(OBJS) $(KRB5_BASE_DEPLIBS)
279 - $(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS)
280 + $(CC_LINK) -o $@ $(OBJS) $(KRB5_BASE_LIBS) $(KSU_LIBS) $(PAM_LIBS)
283 + $(CC) $(ALL_CFLAGS) -c $<
287 diff -up krb5-1.8/src/clients/ksu/pam.c.pam krb5-1.8/src/clients/ksu/pam.c
288 --- krb5-1.8/src/clients/ksu/pam.c.pam 2010-03-05 10:48:08.000000000 -0500
289 +++ krb5-1.8/src/clients/ksu/pam.c 2010-03-05 10:48:08.000000000 -0500
292 + * src/clients/ksu/pam.c
294 + * Copyright 2007,2009,2010 Red Hat, Inc.
296 + * All Rights Reserved.
298 + * Redistribution and use in source and binary forms, with or without
299 + * modification, are permitted provided that the following conditions are met:
301 + * Redistributions of source code must retain the above copyright notice, this
302 + * list of conditions and the following disclaimer.
304 + * Redistributions in binary form must reproduce the above copyright notice,
305 + * this list of conditions and the following disclaimer in the documentation
306 + * and/or other materials provided with the distribution.
308 + * Neither the name of Red Hat, Inc. nor the names of its contributors may be
309 + * used to endorse or promote products derived from this software without
310 + * specific prior written permission.
312 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
313 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
314 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
315 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
316 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
317 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
318 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
319 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
320 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
321 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
322 + * POSSIBILITY OF SUCH DAMAGE.
324 + * Convenience wrappers for using PAM.
327 +#include "autoconf.h"
329 +#include <sys/types.h>
338 +#define MAXPWSIZE 128
341 +static int appl_pam_started;
342 +static pid_t appl_pam_starter = -1;
343 +static int appl_pam_session_opened;
344 +static int appl_pam_creds_initialized;
345 +static int appl_pam_pwchange_required;
346 +static pam_handle_t *appl_pamh;
347 +static struct pam_conv appl_pam_conv;
348 +static char *appl_pam_user;
349 +struct appl_pam_non_interactive_args {
351 + const char *password;
355 +appl_pam_enabled(krb5_context context, const char *section)
358 + if ((context != NULL) && (context->profile != NULL)) {
359 + if (profile_get_boolean(context->profile,
361 + USE_PAM_CONFIGURATION_KEYWORD,
363 + enabled, &enabled) != 0) {
371 +appl_pam_cleanup(void)
373 + if (getpid() != appl_pam_starter) {
377 + printf("Called to clean up PAM.\n");
379 + if (appl_pam_creds_initialized) {
381 + printf("Deleting PAM credentials.\n");
383 + pam_setcred(appl_pamh, PAM_DELETE_CRED);
384 + appl_pam_creds_initialized = 0;
386 + if (appl_pam_session_opened) {
388 + printf("Closing PAM session.\n");
390 + pam_close_session(appl_pamh, 0);
391 + appl_pam_session_opened = 0;
393 + appl_pam_pwchange_required = 0;
394 + if (appl_pam_started) {
396 + printf("Shutting down PAM.\n");
398 + pam_end(appl_pamh, 0);
399 + appl_pam_started = 0;
400 + appl_pam_starter = -1;
401 + free(appl_pam_user);
402 + appl_pam_user = NULL;
406 +appl_pam_interactive_converse(int num_msg, const struct pam_message **msg,
407 + struct pam_response **presp, void *appdata_ptr)
409 + const struct pam_message *message;
410 + struct pam_response *resp;
412 + char *pwstring, pwbuf[MAXPWSIZE];
413 + unsigned int pwsize;
414 + resp = malloc(sizeof(struct pam_response) * num_msg);
415 + if (resp == NULL) {
416 + return PAM_BUF_ERR;
418 + memset(resp, 0, sizeof(struct pam_response) * num_msg);
419 + code = PAM_SUCCESS;
420 + for (i = 0; i < num_msg; i++) {
421 + message = &(msg[0][i]); /* XXX */
422 + message = msg[i]; /* XXX */
424 + switch (message->msg_style) {
425 + case PAM_TEXT_INFO:
426 + case PAM_ERROR_MSG:
427 + printf("[%s]\n", message->msg ? message->msg : "");
429 + resp[i].resp = NULL;
430 + resp[i].resp_retcode = PAM_SUCCESS;
432 + case PAM_PROMPT_ECHO_ON:
433 + case PAM_PROMPT_ECHO_OFF:
434 + if (message->msg_style == PAM_PROMPT_ECHO_ON) {
435 + if (fgets(pwbuf, sizeof(pwbuf),
437 + pwbuf[strcspn(pwbuf, "\r\n")] = '\0';
441 + pwstring = getpass(message->msg ?
445 + if ((pwstring != NULL) && (pwstring[0] != '\0')) {
446 + pwsize = strlen(pwstring);
447 + resp[i].resp = malloc(pwsize + 1);
448 + if (resp[i].resp == NULL) {
449 + resp[i].resp_retcode = PAM_BUF_ERR;
451 + memcpy(resp[i].resp, pwstring, pwsize);
452 + resp[i].resp[pwsize] = '\0';
453 + resp[i].resp_retcode = PAM_SUCCESS;
456 + resp[i].resp_retcode = PAM_CONV_ERR;
457 + code = PAM_CONV_ERR;
468 +appl_pam_non_interactive_converse(int num_msg,
469 + const struct pam_message **msg,
470 + struct pam_response **presp,
473 + const struct pam_message *message;
474 + struct pam_response *resp;
476 + unsigned int pwsize;
477 + struct appl_pam_non_interactive_args *args;
478 + const char *pwstring;
479 + resp = malloc(sizeof(struct pam_response) * num_msg);
480 + if (resp == NULL) {
481 + return PAM_BUF_ERR;
483 + args = appdata_ptr;
484 + memset(resp, 0, sizeof(struct pam_response) * num_msg);
485 + code = PAM_SUCCESS;
486 + for (i = 0; i < num_msg; i++) {
487 + message = &((*msg)[i]);
490 + switch (message->msg_style) {
491 + case PAM_TEXT_INFO:
492 + case PAM_ERROR_MSG:
494 + case PAM_PROMPT_ECHO_ON:
495 + case PAM_PROMPT_ECHO_OFF:
496 + if (message->msg_style == PAM_PROMPT_ECHO_ON) {
497 + /* assume "user" */
498 + pwstring = args->user;
500 + /* assume "password" */
501 + pwstring = args->password;
503 + if ((pwstring != NULL) && (pwstring[0] != '\0')) {
504 + pwsize = strlen(pwstring);
505 + resp[i].resp = malloc(pwsize + 1);
506 + if (resp[i].resp == NULL) {
507 + resp[i].resp_retcode = PAM_BUF_ERR;
509 + memcpy(resp[i].resp, pwstring, pwsize);
510 + resp[i].resp[pwsize] = '\0';
511 + resp[i].resp_retcode = PAM_SUCCESS;
514 + resp[i].resp_retcode = PAM_CONV_ERR;
515 + code = PAM_CONV_ERR;
526 +appl_pam_start(const char *service, int interactive,
527 + const char *login_username,
528 + const char *non_interactive_password,
529 + const char *hostname,
533 + static int exit_handler_registered;
534 + static struct appl_pam_non_interactive_args args;
536 + if (appl_pam_started &&
537 + (strcmp(login_username, appl_pam_user) != 0)) {
538 + appl_pam_cleanup();
539 + appl_pam_user = NULL;
541 + if (!appl_pam_started) {
543 + printf("Starting PAM up (service=\"%s\",user=\"%s\").\n",
544 + service, login_username);
546 + memset(&appl_pam_conv, 0, sizeof(appl_pam_conv));
547 + appl_pam_conv.conv = interactive ?
548 + &appl_pam_interactive_converse :
549 + &appl_pam_non_interactive_converse;
550 + memset(&args, 0, sizeof(args));
551 + args.user = strdup(login_username);
552 + args.password = non_interactive_password ?
553 + strdup(non_interactive_password) :
555 + appl_pam_conv.appdata_ptr = &args;
556 + ret = pam_start(service, login_username,
557 + &appl_pam_conv, &appl_pamh);
559 + if (hostname != NULL) {
561 + printf("Setting PAM_RHOST to \"%s\".\n", hostname);
563 + pam_set_item(appl_pamh, PAM_RHOST, hostname);
565 + if (ruser != NULL) {
567 + printf("Setting PAM_RUSER to \"%s\".\n", ruser);
569 + pam_set_item(appl_pamh, PAM_RUSER, ruser);
573 + printf("Setting PAM_TTY to \"%s\".\n", tty);
575 + pam_set_item(appl_pamh, PAM_TTY, tty);
577 + if (!exit_handler_registered &&
578 + (atexit(appl_pam_cleanup) != 0)) {
579 + pam_end(appl_pamh, 0);
583 + appl_pam_started = 1;
584 + appl_pam_starter = getpid();
585 + appl_pam_user = strdup(login_username);
586 + exit_handler_registered = 1;
593 +appl_pam_acct_mgmt(const char *service, int interactive,
594 + const char *login_username,
595 + const char *non_interactive_password,
596 + const char *hostname,
601 + appl_pam_pwchange_required = 0;
602 + ret = appl_pam_start(service, interactive, login_username,
603 + non_interactive_password, hostname, ruser, tty);
606 + printf("Calling pam_acct_mgmt().\n");
608 + ret = pam_acct_mgmt(appl_pamh, 0);
613 + case PAM_NEW_AUTHTOK_REQD:
614 + appl_pam_pwchange_required = 1;
624 +appl_pam_requires_chauthtok(void)
626 + return appl_pam_pwchange_required;
629 +appl_pam_session_open(void)
632 + if (appl_pam_started) {
634 + printf("Opening PAM session.\n");
636 + ret = pam_open_session(appl_pamh, 0);
638 + appl_pam_session_opened = 1;
644 +appl_pam_setenv(void)
647 +#ifdef HAVE_PAM_GETENVLIST
651 + if (appl_pam_started) {
652 + list = pam_getenvlist(appl_pamh);
653 + for (i = 0; ((list != NULL) && (list[i] != NULL)); i++) {
655 + printf("Setting \"%s\" in environment.\n", list[i]);
665 +appl_pam_cred_init(void)
668 + if (appl_pam_started) {
670 + printf("Initializing PAM credentials.\n");
672 + ret = pam_setcred(appl_pamh, PAM_ESTABLISH_CRED);
674 + appl_pam_creds_initialized = 1;
680 diff -up krb5-1.8/src/clients/ksu/pam.h.pam krb5-1.8/src/clients/ksu/pam.h
681 --- krb5-1.8/src/clients/ksu/pam.h.pam 2010-03-05 10:48:08.000000000 -0500
682 +++ krb5-1.8/src/clients/ksu/pam.h 2010-03-05 10:48:08.000000000 -0500
685 + * src/clients/ksu/pam.h
687 + * Copyright 2007,2009,2010 Red Hat, Inc.
689 + * All Rights Reserved.
691 + * Redistribution and use in source and binary forms, with or without
692 + * modification, are permitted provided that the following conditions are met:
694 + * Redistributions of source code must retain the above copyright notice, this
695 + * list of conditions and the following disclaimer.
697 + * Redistributions in binary form must reproduce the above copyright notice,
698 + * this list of conditions and the following disclaimer in the documentation
699 + * and/or other materials provided with the distribution.
701 + * Neither the name of Red Hat, Inc. nor the names of its contributors may be
702 + * used to endorse or promote products derived from this software without
703 + * specific prior written permission.
705 + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
706 + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
707 + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
708 + * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
709 + * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
710 + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
711 + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
712 + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
713 + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
714 + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
715 + * POSSIBILITY OF SUCH DAMAGE.
717 + * Convenience wrappers for using PAM.
721 +#ifdef HAVE_SECURITY_PAM_APPL_H
722 +#include <security/pam_appl.h>
725 +#define USE_PAM_CONFIGURATION_KEYWORD "use_pam"
728 +int appl_pam_enabled(krb5_context context, const char *section);
729 +int appl_pam_acct_mgmt(const char *service, int interactive,
730 + const char *local_username,
731 + const char *non_interactive_password,
732 + const char *hostname,
735 +int appl_pam_requires_chauthtok(void);
736 +int appl_pam_session_open(void);
737 +int appl_pam_setenv(void);
738 +int appl_pam_cred_init(void);
739 +void appl_pam_cleanup(void);
741 diff -up krb5-1.8/src/configure.in.pam krb5-1.8/src/configure.in
742 --- krb5-1.8/src/configure.in.pam 2009-12-31 18:13:56.000000000 -0500
743 +++ krb5-1.8/src/configure.in 2010-03-05 10:48:08.000000000 -0500
744 @@ -1051,6 +1051,8 @@ if test "$ac_cv_lib_socket" = "yes" -a "
745 AC_SUBST([VERTO_LIBS])
746 AC_SUBST([VERTO_VERSION])
750 # Make localedir work in autoconf 2.5x.
751 if test "${localedir+set}" != set; then
752 localedir='$(datadir)/locale'