2
0
mirror of https://gitlab.com/apparmor/apparmor synced 2025-08-22 01:57:43 +00:00

This (updated) patch provides some limited configurability for

pam_apparmor pam module. The default behavior is to use the user's
primary groupname, and to fall back to the DEFAULT hat. You can change
this behavior by appending order=type1[,type2,type3] to the pam_apparmor
session line in the pam config for the application you're applying
pam_apparmor to. The available types are 'user' for username, 'group'
for groupname, and 'default' for DEFAULT. Thus, adding a configuration
entry like:

  session  optional       pam_apparmor.so order=group,default

is equivalent to the default behavior for pam_apparmor.

The parse_option code got a little more complicated than I'd hoped
it would be; I could have just had types by space delimited options to
module, but I thought I'd leave open the possibility of adding additional
options to the module ('debug' immediately comes to mind).

I disabled the short-circuit that occurs if EPERM is returned by
change_hat, as we can't detect that this is because there's no hats or
that the application is entirely undefined; if ECHILD makes it in then
we can re-enable this.

I am less convinced now that pam_apparmor needs to be 'optional' than
'required'; killing the session if none of the change_hats succeeds is
starting to feel like reasonable behavior.

---
 changehat/pam_apparmor/Makefile             |   11 +
 changehat/pam_apparmor/README               |   74 +++++++++++++
 changehat/pam_apparmor/get_options.c        |  157 ++++++++++++++++++++++++++++
 changehat/pam_apparmor/pam_apparmor.c       |  155 +++++++++++++++++++--------
 changehat/pam_apparmor/pam_apparmor.h       |   56 +++++++++
 changehat/pam_apparmor/pam_apparmor.spec.in |    2 
 6 files changed, 406 insertions(+), 49 deletions(-)
This commit is contained in:
Steve Beattie 2006-10-31 15:54:47 +00:00
parent 63712f92db
commit 36523dc023
6 changed files with 408 additions and 51 deletions

View File

@ -26,13 +26,18 @@ Make.rules: $(COMMONDIR)/Make.rules
ln -f $(COMMONDIR)/Make.rules .
endif
EXTRA_CFLAGS=$(CFLAGS) -fPIC -shared -Xlinker -x
EXTRA_CFLAGS=$(CFLAGS) -fPIC -shared -Wall
LINK_FLAGS=-Xlinker -x
LIBS=-lpam -lapparmor
OBJECTS=${NAME}.o get_options.o
all: $(NAME).so
$(NAME).so: $(NAME).c
$(CC) $(EXTRA_CFLAGS) -o $@ $< $(LIBS)
$(NAME).so: ${OBJECTS}
$(CC) $(EXTRA_CFLAGS) $(LINK_FLAGS) -o $@ ${OBJECTS} $(LIBS)
%.o: %.c
$(CC) $(EXTRA_CFLAGS) -c -o $@ $<
# need some better way of determining this
DESTDIR=/

View File

@ -1 +1,73 @@
XXX - PUT DOCUMENTATION ABOUT PAM_APPARMOR HERE - :)
pam_apparmor - a (linux specific) PAM module to add support for
apparmor's subprocess confinement.
An apparmor profile applies to an executable program; if a portion of
the program needs different access permissions than other portions, the
program can "change hats" via change_hat(2) to a different role, also
known as a subprofile. The pam_apparmor PAM module allows applications
to confine authenticated users into subprofiles based on groupnames,
usernames, or a default profile. To accomplish this, pam_apparmor needs
to be registered as a PAM session module.
Compiling pam_apparmor
----------------------
The pam-development libraries and libapparmor need to be installed
on the build system. 'make' should be all that is needed to build
pam_apparmor.so; 'make rpm' should work on RPM-based systems.
Configuring pam_apparmor
------------------------
To add pam_apparmor support to a pam enabled application, add a line
like the following to the pam configuration file for the application
(usually stored in /etc/pam.d/):
session optional pam_apparmor.so
Likely you will want add the pam_apparmor after other session management
modules. If you make the pam_apparmor module 'required' instead of
'optional', the session will abort if pam_apparmor is not able to
successfully find a hat to change_hat into. Be careful when making it
required; it is possible to cause all attempted logins to the service to
fail if the apparmor policy is insufficient.
By default, pam_apparmor will attempt to change_hat into a hat based
on the primary group name of the user logging in. If that hat fails to
exist, the module will attempt to change_hat into a hat named DEFAULT
(it is recommended to ensure this hat exists in the apparmor profiles
for applications using pam_apparmor).
However, this is configurable by adding an option to the pam configuration
line to modify what order and what attributes pam_apparmor will attempt
to use when attempting to change_hat. To do so, add 'order=' followed by
a comma seperated list of types of hats to try. The type of hats
available are:
* 'user' - the username will be used as the hatname
* 'group' - the primary group will be used as the hatname
* 'default' - the string 'DEFAULT' will be used as the hatname.
Generally, this should be the hat of last resort.
The order in the list determines the order the hat will be attempted.
Some exmaple configurations:
# the default behavior
session optional pam_apparmor.so order=group,default
# attempt to use only the usernmae
session optional pam_apparmor.so order=user
# use the usernmae, followed by the primary groupname, follwed by
# DEFAULT if the prior hats do not exist in the apparmor profile
session optional pam_apparmor.so order=user,group,default
References
----------
Project webpage:
http://developer.novell.com/wiki/index.php/Novell_AppArmor
To provide feedback or ask questions please contact the
apparmor-dev@forge.novell.com mail list. This is the development list
for the AppArmor team.
See also: change_hat(3), and the Linux-PAM online documentation at
http://www.kernel.org/pub/linux/libs/pam/Linux-PAM-html/

View File

@ -0,0 +1,157 @@
/*
* $Id: $
*
* Written by Steve Beattie <sbeattie@suse.de> 2006/10/25
*
* Modeled after the option parsing code in pam_unix2 by:
* Copyright (c) 2006 SUSE Linux Products GmbH, Nuernberg, Germany.
* Copyright (c) 2002, 2003, 2004 SuSE GmbH Nuernberg, Germany.
* Author: Thorsten Kukuk <kukuk@suse.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, and the entire permission notice in its entirety,
* including the disclaimer of warranties.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* ALTERNATIVELY, this product may be distributed under the terms of
* the GNU Public License, in which case the provisions of the GPL are
* required INSTEAD OF the above restrictions. (This clause is
* necessary due to a potential bad interaction between the GPL and
* the restrictions contained in a BSD-style copyright.)
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE /* for strndup() */
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <syslog.h>
#include <errno.h>
#include <security/pam_ext.h>
#define PAM_SM_SESSION
#include <security/pam_modules.h>
#include "pam_apparmor.h"
#define DEBUG 1
#define ORDER_PREFIX "order="
static int parse_option(pam_handle_t *pamh, struct config **config, const char *argv)
{
const char *opts;
if (argv == NULL || argv[0] == '\0')
return 0;
/* someday we may have more option. Gasp! */
if (strncasecmp(argv, ORDER_PREFIX, strlen(ORDER_PREFIX)) != 0) {
pam_syslog (pamh, LOG_ERR, "Unknown option: `%s'\n", argv);
return PAM_SESSION_ERR;
}
opts = argv + strlen(ORDER_PREFIX);
while (*opts != '\0') {
hat_t hat;
char *opt, *comma;
int i;
comma = index(opts, ',');
if (comma)
opt = strndup(opts, comma - opts);
else
opt = strdup(opts);
if (!opt) {
pam_syslog(pamh, LOG_ERR, "Memory allocation error: %s",
strerror(errno));
return PAM_SESSION_ERR;
}
if (strcasecmp(opt, "group") == 0)
hat = eGroupname;
else if (strcasecmp(opt, "user") == 0)
hat = eUsername;
else if (strcasecmp(opt, "default") == 0)
hat = eDefault;
else {
pam_syslog (pamh, LOG_ERR, "Unknown option: `%s'\n", opt);
free(opt);
return PAM_SESSION_ERR;
}
if (!(*config)) {
struct config *new_cfg = malloc(sizeof(**config));
if (!new_cfg) {
pam_syslog(pamh, LOG_ERR, "Memory allocation error: %s",
strerror(errno));
free(opt);
return PAM_SESSION_ERR;
}
new_cfg->hat_type[0] = eNoEntry;
new_cfg->hat_type[1] = eNoEntry;
new_cfg->hat_type[2] = eNoEntry;
(*config) = new_cfg;
}
/* Find free table entry, looking for duplicates */
for (i = 0; i < MAX_HAT_TYPES && (*config)->hat_type[i] != eNoEntry; i++) {
if ((*config)->hat_type[i] == hat) {
pam_syslog(pamh, LOG_ERR, "Duplicate hat type: %s\n", opt);
free(opt);
free(*config);
(*config) = NULL;
return PAM_SESSION_ERR;
}
}
if (i >= MAX_HAT_TYPES) {
pam_syslog(pamh, LOG_ERR, "Unable to add hat type '%s'\n", opt);
return PAM_SESSION_ERR;
}
(*config)->hat_type[i] = hat;
free(opt);
if (comma)
opts = comma + 1;
else
opts += strlen(opts);
}
return 0;
}
int get_options(pam_handle_t *pamh, struct config **config, int argc, const char **argv)
{
int retval = 0;
/* Parse parameters for module */
for ( ; argc-- > 0; argv++) {
int rc = parse_option(pamh, config, *argv);
if (rc != 0)
retval = rc;
}
return retval;
}

View File

@ -4,6 +4,7 @@
* $Id$
*
* Written by Jesse Michael <jmichael@suse.de> 2006/08/24
* and Steve Beattie <sbeattie@suse.de> 2006/10/25
*
* Based off of pam_motd by:
* Ben Collins <bcollins@debian.org> 2005/10/04
@ -21,8 +22,9 @@
#include <pwd.h>
#include <grp.h>
#include <syslog.h>
#include <security/_pam_macros.h>
#include <errno.h>
#include <sys/apparmor.h>
#include <security/pam_ext.h>
/*
* here, we make a definition for the externally accessible function
@ -32,18 +34,25 @@
*/
#define PAM_SM_SESSION
#include <security/pam_modules.h>
#include "pam_apparmor.h"
#define DEBUG 0
static struct config default_config = {
.hat_type[0] = eGroupname,
.hat_type[1] = eDefault,
.hat_type[2] = eNoEntry,
};
/* --- session management functions (only) --- */
PAM_EXTERN int
pam_sm_close_session (pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
return PAM_IGNORE;
return PAM_IGNORE;
}
PAM_EXTERN
@ -55,6 +64,14 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags,
const char *user;
struct passwd *pw;
struct group *gr;
struct config *config = NULL;
int i;
if ((retval = get_options(pamh, &config, argc, argv)) != 0)
return retval;
if (!config)
config = &default_config;
/* grab the target user name */
retval = pam_get_user(pamh, &user, NULL);
@ -64,13 +81,13 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags,
}
pw = getpwnam(user);
if(!pw) {
if (!pw) {
pam_syslog(pamh, LOG_ERR, "Can't determine group for user %s\n", user);
return PAM_PERM_DENIED;
}
gr = getgrgid(pw->pw_gid);
if(!gr || !gr->gr_name) {
if (!gr || !gr->gr_name) {
pam_syslog(pamh, LOG_ERR, "Can't read info for group %d\n", pw->pw_gid);
return PAM_PERM_DENIED;
}
@ -81,10 +98,8 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags,
return PAM_PERM_DENIED;
}
/*
* the magic token needs to be non-zero otherwise, we won't be able
* to probe for hats
*/
/* the magic token needs to be non-zero otherwise, we won't be able
* to probe for hats */
do {
retval = read(fd, (void *) &magic_token, sizeof(magic_token));
if (retval < 0) {
@ -95,50 +110,102 @@ int pam_sm_open_session(pam_handle_t *pamh, int flags,
close(fd);
/* change into the group hat */
retval = change_hat(gr->gr_name, magic_token);
if (retval < 0) {
/* failed to change into group hat, so we'll jump back out */
retval = change_hat(NULL, magic_token);
if (retval == 0) {
/* and try to change to the DEFAULT hat instead */
retval = change_hat("DEFAULT", magic_token);
if (retval < 0) {
/*
* failed to change into default hat, so
* we'll jump back out
*/
retval = change_hat(NULL, magic_token);
pam_syslog(pamh, LOG_ERR, "Can't change to DEFAULT hat\n");
pam_retval = PAM_PERM_DENIED;
} else {
pam_retval = PAM_SUCCESS;
for (i = 0; i < MAX_HAT_TYPES && config->hat_type[i] != eNoEntry; i++) {
const char *hat = NULL;
switch (config->hat_type[i]) {
case eGroupname:
#if DEBUG
pam_syslog(pamh, LOG_DEBUG, "Successfully changed to DEFAULT hat\n");
pam_syslog(pamh, LOG_DEBUG, "Using groupname\n");
#endif
}
} else {
/*
* changing into the group hat and attempting to
* jump back out both failed. that most likely
* means that either apparmor is not loaded or we
* don't have a profile loaded for this application.
* in this case, we want to allow the pam operation
* to succeed.
*/
hat = gr->gr_name;
break;
case eUsername:
#if DEBUG
pam_syslog(pamh, LOG_DEBUG, "Using username\n");
#endif
hat = user;
break;
case eDefault:
#if DEBUG
pam_syslog(pamh, LOG_DEBUG, "Using DEFAULT\n");
#endif
hat = "DEFAULT";
break;
default:
pam_syslog(pamh, LOG_ERR, "Unknown value in hat table: %x\n",
config->hat_type[i]);
goto nodefault;
break;
}
} else {
retval = change_hat(hat, magic_token);
if (retval == 0) {
/* success, let's bail */
#if DEBUG
pam_syslog(pamh, LOG_DEBUG, "Successfully changed to %s hat\n", gr->gr_name);
pam_syslog(pamh, LOG_DEBUG, "Successfully changed to hat '%s'\n", hat);
#endif
goto out;
}
switch (errno) {
/* case EPERM: */ /* Can't enable until ECHILD patch gets accepted, and we can
* distinguish between unconfined and confined-but-no-hats */
case EINVAL:
/* apparmor is not loaded or application is unconfined,
* stop attempting to use change_hat */
#if DEBUG
pam_syslog(pamh, LOG_DEBUG,
"AppArmor not loaded, or application is unconfined\n");
#endif
pam_retval = PAM_SUCCESS;
goto out;
break;
case ECHILD:
/* application is confined but has no hats,
* stop attempting to use change_hat */
goto nodefault;
break;
case EPERM: /* Disable when ECHILD patch gets accepted */
case EACCES:
/* failed to change into attempted hat, so we'll
* jump back out and try the next one */
break;
default:
pam_syslog(pamh, LOG_ERR, "Unknown error occurred changing to %s hat: %s\n",
hat, strerror(errno));
/* give up? */
pam_retval = PAM_SYSTEM_ERR;
goto out;
}
retval = change_hat(NULL, magic_token);
if (retval != 0) {
/* changing into the specific hat and attempting to
* jump back out both failed. that most likely
* means that either apparmor is not loaded or we
* don't have a profile loaded for this application.
* in this case, we want to allow the pam operation
* to succeed. */
goto out;
}
}
/*
* zero out the magic token so an attacker wouldn't be able to
* just grab it out of process memory and instead would need to
* brute force it
*/
memset(&magic_token, 0, sizeof(magic_token));
nodefault:
/* if we got here, we were unable to change into any of the hats
* we attempted. */
pam_syslog(pamh, LOG_ERR, "Can't change to any hat\n");
pam_retval = PAM_SESSION_ERR;
out:
/* zero out the magic token so an attacker wouldn't be able to
* just grab it out of process memory and instead would need to
* brute force it */
memset(&magic_token, 0, sizeof(magic_token));
if (config && config != &default_config)
free(config);
return pam_retval;
}

View File

@ -0,0 +1,56 @@
/* pam_apparmor module */
/*
* $Id: $
*
* Written by Jesse Michael <jmichael@suse.de> 2006/08/24
* and Steve Beattie <sbeattie@suse.de> 2006/10/25
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, and the entire permission notice in its entirety,
* including the disclaimer of warranties.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The name of the author may not be used to endorse or promote
* products derived from this software without specific prior
* written permission.
*
* ALTERNATIVELY, this product may be distributed under the terms of
* the GNU Public License, in which case the provisions of the GPL are
* required INSTEAD OF the above restrictions. (This clause is
* necessary due to a potential bad interaction between the GPL and
* the restrictions contained in a BSD-style copyright.)
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
* INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
enum hat_t {
eNoEntry,
eUsername,
eGroupname,
eDefault,
};
typedef enum hat_t hat_t;
#define MAX_HAT_TYPES 3
struct config {
hat_t hat_type[MAX_HAT_TYPES];
};
extern int get_options(pam_handle_t *pamh, struct config **config,
int argc, const char **argv);

View File

@ -29,7 +29,7 @@ Prereq: pam
%description
The pam_apparmor module provides the means for any pam applications that
call pam_open_session() to automatically perform an AppArmor change_hat
call pam_open_session() to automatically perform an AppArmor change_hat
operation in order to switch to a user-specific security policy.