1 /******************************************************************************
2 * A module for Linux-PAM that will set the default security context after login
5 * Copyright (c) 2003 Red Hat, Inc.
6 * Written by Dan Walsh <dwalsh@redhat.com>
8 * Redistribution and use in source and binary forms, with or without
9 * modification, are permitted provided that the following conditions
11 * 1. Redistributions of source code must retain the above copyright
12 * notice, and the entire permission notice in its entirety,
13 * including the disclaimer of warranties.
14 * 2. Redistributions in binary form must reproduce the above copyright
15 * notice, this list of conditions and the following disclaimer in the
16 * documentation and/or other materials provided with the distribution.
17 * 3. The name of the author may not be used to endorse or promote
18 * products derived from this software without specific prior
21 * ALTERNATIVELY, this product may be distributed under the terms of
22 * the GNU Public License, in which case the provisions of the GPL are
23 * required INSTEAD OF the above restrictions. (This clause is
24 * necessary due to a potential bad interaction between the GPL and
25 * the restrictions contained in a BSD-style copyright.)
27 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
28 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
29 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
30 * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
31 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
32 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
33 * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
34 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
35 * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
37 * OF THE POSSIBILITY OF SUCH DAMAGE.
50 #include <sys/types.h>
53 #include <linux/limits.h>
56 #define PAM_SM_SESSION
58 #include <security/pam_modules.h>
59 #include <security/_pam_macros.h>
60 #include <security/_pam_modutil.h>
63 #define _(x) gettext(x)
65 #ifndef PAM_SELINUX_MAIN
66 #define MODULE "pam_selinux"
68 #include <selinux/selinux.h>
69 #include <selinux/get_context_list.h>
70 #include <selinux/flask.h>
71 #include <selinux/selinux.h>
72 #include <selinux/context.h>
75 send_text (const struct pam_conv *conv, const char *text, int debug)
77 struct pam_message message;
78 const struct pam_message *messages[] = {&message};
79 struct pam_response *responses;
82 memset(&message, 0, sizeof(message));
83 message.msg_style = PAM_TEXT_INFO;
86 syslog(LOG_NOTICE, MODULE ": %s", message.msg);
87 retval = conv->conv(1, messages, &responses, conv->appdata_ptr);
89 _pam_drop_reply(responses, 1);
94 * This function sends a message to the user and gets the response. The caller
95 * is responsible for freeing the responses.
98 query_response (const struct pam_conv *conv, const char *text,
99 struct pam_response **responses, int debug)
101 struct pam_message message;
102 const struct pam_message *messages[] = {&message};
104 memset(&message, 0, sizeof(message));
105 message.msg_style = PAM_PROMPT_ECHO_ON;
109 syslog(LOG_NOTICE, MODULE ": %s", message.msg);
111 return conv->conv(1, messages, responses, conv->appdata_ptr);
114 static security_context_t
115 select_context (pam_handle_t *pamh, security_context_t* contextlist,
118 const void *void_conv;
119 const struct pam_conv *conv;
121 if (pam_get_item(pamh, PAM_CONV, &void_conv) == PAM_SUCCESS &&
124 if (conv->conv != NULL) {
125 struct pam_response *responses;
126 char *text=calloc(PATH_MAX,1);
129 return (security_context_t) strdup(contextlist[0]);
131 snprintf(text, PATH_MAX,
132 _("Your default context is %s. \n"), contextlist[0]);
133 send_text(conv,text,debug);
135 query_response(conv,_("Do you want to choose a different one? [n]"),
137 if (responses && ((responses[0].resp[0] == 'y') ||
138 (responses[0].resp[0] == 'Y')))
142 char *prompt=_("Enter number of choice: ");
143 int len=strlen(prompt);
146 _pam_drop_reply(responses, 1);
147 for (i = 0; contextlist[i]; i++) {
148 len+=strlen(contextlist[i]) + 10;
151 for (i = 0; contextlist[i]; i++) {
152 snprintf(buf, PATH_MAX,
153 "[%d] %s\n", i+1, contextlist[i]);
154 strncat(text,buf,len);
157 while ((choice < 1) || (choice > i)) {
158 query_response(conv,text,&responses,debug);
159 choice = strtol (responses[0].resp, NULL, 10);
160 _pam_drop_reply(responses, 1);
163 return (security_context_t) strdup(contextlist[choice-1]);
166 _pam_drop_reply(responses, 1);
169 syslog(LOG_NOTICE, _("%s: bogus conversation function"),MODULE);
173 syslog(LOG_NOTICE, _("%s: no conversation function"),MODULE);
175 return (security_context_t) strdup(contextlist[0]);
178 static security_context_t
179 manual_context (pam_handle_t *pamh, const char *user, int debug)
181 const void *void_conv;
182 const struct pam_conv *conv;
183 security_context_t newcon;
184 context_t new_context;
185 int mls_enabled = is_selinux_mls_enabled();
187 if (pam_get_item(pamh, PAM_CONV, &void_conv) == PAM_SUCCESS) {
189 if (conv && conv->conv != NULL) {
190 struct pam_response *responses;
194 _("Would you like to enter a security context? [y] "),
196 if ((responses[0].resp[0] == 'y') || (responses[0].resp[0] == 'Y') ||
197 (responses[0].resp[0] == '\0') )
200 new_context = context_new ("user:role:type:level");
202 new_context = context_new ("user:role:type");
203 _pam_drop_reply(responses, 1);
205 /* Allow the user to enter each field of the context individually */
206 if (context_user_set (new_context, user))
208 context_free (new_context);
211 query_response(conv,_("role: "),&responses,debug);
212 if (context_role_set (new_context, responses[0].resp))
214 _pam_drop_reply(responses, 1);
215 context_free (new_context);
218 _pam_drop_reply(responses, 1);
219 query_response(conv,_("type: "),&responses,debug);
220 if (context_type_set (new_context, responses[0].resp))
222 _pam_drop_reply(responses, 1);
223 context_free (new_context);
226 _pam_drop_reply(responses, 1);
229 query_response(conv,_("level: "),&responses,debug);
230 if (context_range_set (new_context, responses[0].resp))
232 context_free (new_context);
236 /* Get the string value of the context and see if it is valid. */
237 if (!security_check_context(context_str(new_context))) {
238 newcon = strdup(context_str(new_context));
239 context_free (new_context);
243 send_text(conv,_("Not a valid security context"),debug);
246 _pam_drop_reply(responses, 1);
252 syslog(LOG_NOTICE, _("%s: bogus conversation function"),MODULE);
256 syslog(LOG_NOTICE, _("%s: no conversation function"),MODULE);
261 static void security_restorelabel_tty(const char *tty,
262 security_context_t context) {
263 char ttybuf[PATH_MAX];
269 if(strncmp("/dev/", tty, 5)) {
270 snprintf(ttybuf,sizeof(ttybuf),"/dev/%s",tty);
276 if (setfilecon(ptr, context) && errno != ENOENT)
279 _("Warning! Could not relabel %s with %s, not relabeling.\n"),
284 static security_context_t security_label_tty(char *tty,
285 security_context_t usercon) {
286 char ttybuf[PATH_MAX];
288 security_context_t newdev_context=NULL; /* The new context of a device */
289 security_context_t prev_context=NULL; /* The new context of a device */
292 if(strncmp("/dev/", tty, 5))
294 snprintf(ttybuf,sizeof(ttybuf),"/dev/%s",tty);
300 if (getfilecon(ptr, &prev_context) < 0)
303 _("Warning! Could not get current context for %s, not relabeling."), ptr);
306 if( security_compute_relabel(usercon,prev_context,SECCLASS_CHR_FILE,
310 _("Warning! Could not get new context for %s, not relabeling."),
312 syslog(LOG_NOTICE, "usercon=%s, prev_context=%s\n", usercon, prev_context);
313 freecon(prev_context);
316 status=setfilecon(ptr,newdev_context);
320 _("Warning! Could not relabel %s with %s, not relabeling.%s"),
321 ptr,newdev_context,strerror(errno));
322 freecon(prev_context);
325 freecon(newdev_context);
329 static security_context_t user_context=NULL;
330 static security_context_t prev_user_context=NULL;
331 static security_context_t ttyn_context=NULL; /* The current context of ttyn device */
332 static int selinux_enabled=0;
333 static char *ttyn=NULL;
335 /* Tell the user that access has been granted. */
337 verbose_message(pam_handle_t *pamh, char *msg, int debug)
339 const void *void_conv;
340 const struct pam_conv *conv;
341 struct pam_message message;
342 const struct pam_message *messages[] = {&message};
343 struct pam_response *responses;
344 if (pam_get_item(pamh, PAM_CONV, &void_conv) == PAM_SUCCESS) {
346 if (conv && conv->conv != NULL) {
349 memset(&message, 0, sizeof(message));
350 message.msg_style = PAM_TEXT_INFO;
351 snprintf(text, sizeof(text), msg);
355 syslog(LOG_NOTICE, MODULE ": %s", message.msg);
356 conv->conv(1, messages, &responses, conv->appdata_ptr);
358 _pam_drop_reply(responses, 1);
361 syslog(LOG_NOTICE, _("%s: bogus conversation function"),MODULE);
365 syslog(LOG_NOTICE,_("%s: no conversation function"),MODULE);
370 pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
372 /* Fail by default. */
377 pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char **argv)
383 pam_sm_open_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
385 int i, debug = 0, ttys=1, has_tty=isatty(0), verbose=0, multiple=0, close_session=0;
387 security_context_t* contextlist = NULL;
388 int num_contexts = 0;
389 const void *username = NULL;
390 const void *tty = NULL;
392 /* Parse arguments. */
393 for (i = 0; i < argc; i++) {
394 if (strcmp(argv[i], "debug") == 0) {
397 if (strcmp(argv[i], "nottys") == 0) {
400 if (strcmp(argv[i], "verbose") == 0) {
403 if (strcmp(argv[i], "multiple") == 0) {
406 if (strcmp(argv[i], "close") == 0) {
412 syslog(LOG_NOTICE, MODULE ": %s", "Open Session");
414 /* this module is only supposed to execute close_session */
418 if (!(selinux_enabled = is_selinux_enabled()>0) )
421 if (pam_get_item(pamh, PAM_USER, &username) != PAM_SUCCESS ||
425 num_contexts = get_ordered_context_list(username, 0, &contextlist);
426 if (num_contexts > 0) {
427 if (multiple && (num_contexts > 1) && has_tty) {
428 user_context = select_context(pamh,contextlist, debug);
429 freeconary(contextlist);
431 user_context = (security_context_t) strdup(contextlist[0]);
432 freeconary(contextlist);
436 user_context = manual_context(pamh,username,debug);
437 if (user_context == NULL) {
438 syslog (LOG_ERR, _("Unable to get valid context for %s"),
439 (const char *)username);
444 _("Unable to get valid context for %s, No valid tty"),
445 (const char *)username);
449 if (getexeccon(&prev_user_context)<0) {
450 prev_user_context=NULL;
453 /* Get the name of the terminal. */
454 if (pam_get_item(pamh, PAM_TTY, &tty) != PAM_SUCCESS) {
458 if ((tty == NULL) || (strlen(tty) == 0) ||
459 strcmp(tty, "ssh") == 0 || strncmp(tty, "NODEV", 5) == 0) {
460 tty = ttyname(STDIN_FILENO);
461 if ((tty == NULL) || (strlen(tty) == 0)) {
462 tty = ttyname(STDOUT_FILENO);
464 if ((tty == NULL) || (strlen(tty) == 0)) {
465 tty = ttyname(STDERR_FILENO);
471 ttyn_context=security_label_tty(ttyn,user_context);
473 ret = setexeccon(user_context);
474 if (ret==0 && verbose) {
476 snprintf(msg, sizeof(msg),
477 _("Security Context %s Assigned"), user_context);
478 verbose_message(pamh, msg, debug);
481 syslog(LOG_ERR, _("Error! Unable to set %s executable context %s."),
482 (const char *)username, user_context);
483 freecon(user_context);
487 syslog(LOG_NOTICE, _("%s: set %s security context to %s"),MODULE,
488 (const char *)username, user_context);
490 freecon(user_context);
496 pam_sm_close_session(pam_handle_t *pamh, int flags, int argc, const char **argv)
498 int i, debug = 0,status=0, open_session=0;
499 if (! (selinux_enabled ))
502 /* Parse arguments. */
503 for (i = 0; i < argc; i++) {
504 if (strcmp(argv[i], "debug") == 0) {
507 if (strcmp(argv[i], "open") == 0) {
513 syslog(LOG_NOTICE, MODULE ": %s", "Close Session");
520 syslog(LOG_NOTICE, MODULE ":Restore tty %s -> %s", ttyn,ttyn_context);
522 security_restorelabel_tty(ttyn,ttyn_context);
523 freecon(ttyn_context);
527 status=setexeccon(prev_user_context);
528 freecon(prev_user_context);
530 syslog(LOG_ERR, _("Error! Unable to set executable context %s."),
536 syslog(LOG_NOTICE, _("%s: setcontext back to orginal"),MODULE);
541 #else /* PAM_SELINUX_MAIN */
543 /************************************************************************
545 * All PAM code goes in this section.
547 ************************************************************************/
549 #include <unistd.h> /* for getuid(), exit(), getopt() */
551 #include <sys/wait.h> /* for wait() */
553 #include <security/pam_appl.h> /* for PAM functions */
554 #include <security/pam_misc.h> /* for misc_conv PAM utility function */
556 #define SERVICE_NAME "pam_selinux_check" /* the name of this program for PAM */
557 /* The file containing the context to run
558 * the scripts under. */
559 int authenticate_via_pam( const char *user , pam_handle_t **pamh);
561 /* authenticate_via_pam()
565 * return: value condition
567 * 1 pam thinks that the user authenticated themselves properly
570 * this function uses pam to authenticate the user running this
571 * program. this is the only function in this program that makes pam
576 int authenticate_via_pam( const char *user , pam_handle_t **pamh) {
578 struct pam_conv *conv;
579 int result = 0; /* our result, set to 0 (not authenticated) by default */
581 /* this is a jump table of functions for pam to use when it wants to *
582 * communicate with the user. we'll be using misc_conv(), which is *
583 * provided for us via pam_misc.h. */
584 struct pam_conv pam_conversation = {
588 conv = &pam_conversation;
591 /* make `p_pam_handle' a valid pam handle so we can use it when *
592 * calling pam functions. */
593 if( PAM_SUCCESS != pam_start( SERVICE_NAME,
597 fprintf( stderr, _("failed to initialize PAM\n") );
601 if( PAM_SUCCESS != pam_set_item(*pamh, PAM_RUSER, user))
603 fprintf( stderr, _("failed to pam_set_item()\n") );
607 /* Ask PAM to authenticate the user running this program */
608 if( PAM_SUCCESS == pam_authenticate(*pamh,0) ) {
609 if ( PAM_SUCCESS == pam_open_session(*pamh, 0) )
610 result = 1; /* user authenticated OK! */
614 } /* authenticate_via_pam() */
616 int main(int argc, char **argv) {
620 if (!authenticate_via_pam(argv[1],&pamh))
627 /* error in fork() */
628 fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
629 pam_close_session(pamh, 0);
630 /* We're done with PAM. Free `pam_handle'. */
631 pam_end( pamh, PAM_SUCCESS );
635 close(0); close(1); close(2);
637 memset(&sa,0,sizeof(sa));
638 sa.sa_handler = SIG_IGN;
639 sigaction(SIGQUIT, &sa, NULL);
640 sigaction(SIGINT, &sa, NULL);
641 while(wait(NULL) == -1 && errno == EINTR) /**/ ;
642 openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
643 pam_close_session(pamh, 0);
644 /* We're done with PAM. Free `pam_handle'. */
645 pam_end( pamh, PAM_SUCCESS );
651 /* NOTE: The environment has not been sanitized. LD_PRELOAD and other fun
652 * things could be set. */
653 execv("/bin/sh",argv);
654 fprintf(stderr,"Failure\n");