Re: [PATCH] capabilities: introduce per-process capability bounding set (v10)

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

I've pushed it to a pamcap-enhancements branch and I'll will try to
review it quickly.

Thanks

Andrew

KaiGai Kohei wrote:
> Sorry, any TABs are replaced by MUA.
> I'll send the patch again.
> 
>> The attached patch provides several improvement for pam_cap module.
>> 1. It enables pam_cap to drop capabilities from process'es capability
>>    bounding set.
>> 2. It enables to specify allowing inheritable capability set or dropping
>>    bounding capability set for groups, not only users.
>> 3. It provide pam_sm_session() method, not only pam_sm_authenticate()
>>    and pam_sm_setcred(). A system administrator can select more
>>    appropriate mode for his purpose.
>> 4. In the auth/cred mode, it enables to cache the configuration file,
>>    to avoid read and analyze it twice.
>> (Therefore, most of the part in the original one got replaced....)
>>
>> The default configuration file is "/etc/security/capability.conf".
>> You can describe as follows:
>> --------
>> # kaigai get cap_net_raw and cap_kill, tak get cap_sys_pacct pI.
>> # We can omit "i:" in the head of each line.
>> i:cap_net_raw,cap_kill         kaigai
>> cap_sys_pacct                  tak
>>
>> # ymj and tak lost cap_sys_chroot from cap_bset
>> b:cap_sys_chroot               ymj  tak
>>
>> # Any user within webadm group get cap_net_bind_service pI.
>> i:cap_net_bind_service         @webadm
>>
>> # Any user within users group lost cap_sys_module from cap_bset
>> b:cap_sys_module               @users
>> --------
>>
>> When a user or groups he belongs is on several lines, all configurations
>> are simplly compounded.
>>
>> In the above example, if tak belongs to webadm and users group,
>> he will get cap_sys_pacct and cap_net_bind_service pI, and lost
>> cap_sys_chroot and cap_sys_module from his cap_bset.
>>
>> Thanks,
> 
> Signed-off-by: KaiGai Kohei <[email protected]>
> --
>  pam_cap/capability.conf |    6 +
>  pam_cap/pam_cap.c       |  495 ++++++++++++++++++++++++++++-------------------
>  2 files changed, 305 insertions(+), 196 deletions(-)
> 
> diff --git a/pam_cap/capability.conf b/pam_cap/capability.conf
> index b543142..707cdc3 100644
> --- a/pam_cap/capability.conf
> +++ b/pam_cap/capability.conf
> @@ -24,6 +24,12 @@ cap_setfcap		morgan
>  ## 'everyone else' gets no inheritable capabilities
>  none  *
> 
> +# user 'kaigai' lost CAP_NET_RAW capability from bounding set
> +b:cap_net_raw                   kaigai
> +
> +# group 'acctadm' get CAP_SYS_PACCT inheritable capability
> +i:cap_sys_pacct                 @acctadm
> +
>  ## if there is no '*' entry, all users not explicitly mentioned will
>  ## get all available capabilities. This is a permissive default, and
>  ## probably not what you want...
> diff --git a/pam_cap/pam_cap.c b/pam_cap/pam_cap.c
> index 94c5ebc..a917d5c 100644
> --- a/pam_cap/pam_cap.c
> +++ b/pam_cap/pam_cap.c
> @@ -1,5 +1,6 @@
>  /*
>   * Copyright (c) 1999,2007 Andrew G. Morgan <[email protected]>
> + * Copyright (c) 2007      KaiGai Kohei <[email protected]>
>   *
>   * The purpose of this module is to enforce inheritable capability sets
>   * for a specified user.
> @@ -13,298 +14,400 @@
>  #include <stdarg.h>
>  #include <stdlib.h>
>  #include <syslog.h>
> +#include <pwd.h>
> +#include <grp.h>
> 
>  #include <sys/capability.h>
> +#include <sys/prctl.h>
> 
>  #include <security/pam_modules.h>
>  #include <security/_pam_macros.h>
> 
> +#define MODULE_NAME		"pam_cap"
>  #define USER_CAP_FILE           "/etc/security/capability.conf"
>  #define CAP_FILE_BUFFER_SIZE    4096
>  #define CAP_FILE_DELIMITERS     " \t\n"
> -#define CAP_COMBINED_FORMAT     "%s all-i %s+i"
> -#define CAP_DROP_ALL            "%s all-i"
> +
> +#ifndef PR_CAPBSET_DROP
> +#define PR_CAPBSET_DROP		24
> +#endif
> +
> +extern char const *_cap_names[];
> 
>  struct pam_cap_s {
>      int debug;
>      const char *user;
>      const char *conf_filename;
> +    /* set in read_capabilities_for_user() */
> +    cap_t result;
> +    int do_set_inh : 1;
> +    int do_set_bset : 1;
>  };
> 
> -/* obtain the inheritable capabilities for the current user */
> -
> -static char *read_capabilities_for_user(const char *user, const char *source)
> +/* obtain the inheritable/bounding capabilities for the current user */
> +static int read_capabilities_for_user(struct pam_cap_s *pcs)
>  {
> -    char *cap_string = NULL;
> -    char buffer[CAP_FILE_BUFFER_SIZE], *line;
> +    char buffer[CAP_FILE_BUFFER_SIZE];
>      FILE *cap_file;
> +    struct passwd *pwd;
> +    int line_num = 0;
> +    int rc = -1;	/* PAM_(AUTH|CRED|SESSION)_ERR */
> +
> +    pwd = getpwnam(pcs->user);
> +    if (!pwd) {
> +	syslog(LOG_ERR, "user %s not in passwd entries", pcs->user);
> +	return PAM_AUTH_ERR;
> +    }
> 
> -    cap_file = fopen(source, "r");
> -    if (cap_file == NULL) {
> -	D(("failed to open capability file"));
> -	return NULL;
> +    cap_file = fopen(pcs->conf_filename, "r");
> +    if (!cap_file) {
> +	if (errno == ENOENT) {
> +	    syslog(LOG_NOTICE, "%s is not found",
> +		   pcs->conf_filename);
> +	    return PAM_IGNORE;
> +	} else {
> +	    syslog(LOG_ERR, "unable to open '%s' (%s)",
> +		   pcs->conf_filename, strerror(errno));
> +	    return rc;
> +	}
>      }
> 
> -    while ((line = fgets(buffer, CAP_FILE_BUFFER_SIZE, cap_file))) {
> -	int found_one = 0;
> -	const char *cap_text;
> +    pcs->result = NULL;
> +    while (fgets(buffer, CAP_FILE_BUFFER_SIZE, cap_file) != NULL) {
> +	char *pos, *cap_text;
> +	int matched = 0;
> +	int line_ops = CAP_INHERITABLE;
> 
> -	cap_text = strtok(line, CAP_FILE_DELIMITERS);
> +	line_num++;
> 
> -	if (cap_text == NULL) {
> -	    D(("empty line"));
> -	    continue;
> -	}
> -	if (*cap_text == '#') {
> -	    D(("comment line"));
> +	/* remove comment */
> +	pos = strchr(buffer, '#');
> +	if (pos)
> +	    *pos = '\0';
> +
> +	cap_text = strtok(buffer, CAP_FILE_DELIMITERS);
> +	/* empty line */
> +	if (!cap_text)
>  	    continue;
> +
> +	if (!strncmp(cap_text, "b:", 2)) {
> +	    /* permitted field is used to store bounding set */
> +	    line_ops = CAP_PERMITTED;
> +	    cap_text += 2;
> +	} else if (!strncmp(cap_text, "i:", 2)) {
> +	    cap_text += 2;
>  	}
> 
> -	while ((line = strtok(NULL, CAP_FILE_DELIMITERS))) {
> +	/* check members */
> +	while ((pos = strtok(NULL, CAP_FILE_DELIMITERS)) != NULL) {
> +	    /* wildcard */
> +	    if (!strcmp("*", pos)) {
> +		matched = 1;
> +		break;
> +	    }
> 
> -	    if (strcmp("*", line) == 0) {
> -		D(("wildcard matched"));
> -		found_one = 1;
> -		cap_string = strdup(cap_text);
> +	    /* It it group name? */
> +	    if (*pos == '@') {
> +		struct group *grp;
> +		int i;
> +
> +		pos++;
> +		grp = getgrnam(pos);
> +		if (!grp) {
> +		    if (pcs->debug)
> +			syslog(LOG_DEBUG, "group '%s' not found at line:%d",
> +			       pos, line_num);
> +		    continue;
> +		}
> +
> +		if (pwd->pw_gid == grp->gr_gid) {
> +		    if (pcs->debug)
> +			syslog(LOG_DEBUG, "user %s matched with group %s at line:%d",
> +			       pcs->user, pos, line_num);
> +		    matched = 1;
> +		    break;
> +		}
> +
> +		for (i=0; grp->gr_mem[i]; i++) {
> +		    if (!strcmp(pcs->user, grp->gr_mem[i])) {
> +			if (pcs->debug)
> +			    syslog(LOG_DEBUG, "user %s matched with group %s at line:%d",
> +				   pcs->user, pos, line_num);
> +			matched = 1;
> +			break;
> +		    }
> +		}
> +		syslog(LOG_ERR, "no matching %s", pos);
> +	    } else if (!strcmp(pcs->user, pos)) {
> +		if (pcs->debug)
> +		    syslog(LOG_DEBUG, "user '%s' matched at line:%d",
> +			   pos, line_num);
> +		matched = 1;
>  		break;
>  	    }
> +	}
> +
> +	if (matched) {
> +	    char tmpbuf[CAP_FILE_BUFFER_SIZE];
> +	    cap_t tmp;
> +	    cap_value_t value;
> +	    cap_flag_value_t code;
> +
> +	    if (!pcs->result) {
> +		pcs->result = cap_init();
> +		if (!pcs->result) {
> +		    syslog(LOG_ERR, "unable to allocate cap_t object (%s)",
> +			   strerror(errno));
> +		    goto out;
> +		}
> +	    }
> 
> -	    if (strcmp(user, line) == 0) {
> -		D(("exact match for user"));
> -		found_one = 1;
> -		cap_string = strdup(cap_text);
> +	    switch (line_ops) {
> +	    case CAP_INHERITABLE:
> +		pcs->do_set_inh = 1;
> +		break;
> +	    case CAP_PERMITTED:
> +		pcs->do_set_bset = 1;
>  		break;
>  	    }
> 
> -	    D(("user is not [%s] - skipping", line));
> +	    if (!strcmp(cap_text, "none"))
> +		continue;
> +
> +	    snprintf(tmpbuf, sizeof(tmpbuf), "%s=p", cap_text);
> +	    tmp = cap_from_text(tmpbuf);
> +	    if (!tmp) {
> +		syslog(LOG_ERR, "unable to convert '%s' (%s)",
> +		       tmpbuf, strerror(errno));
> +		cap_free(pcs->result);
> +		pcs->result = NULL;
> +		goto out;
> +	    }
> +
> +	    for (value=0; ;value++) {
> +		if (cap_get_flag(tmp, value, CAP_PERMITTED, &code) < 0)
> +		    break;	/* If value == __CAP_BITS, we get EINVAL */
> +		if (code == CAP_SET)
> +		    cap_set_flag(pcs->result, line_ops, 1, &value, CAP_SET);
> +	    }
> +	    cap_free(tmp);
>  	}
> +    }
> 
> -	cap_text = NULL;
> -	line = NULL;
> +    if (pcs->debug) {
> +	char *tmp = cap_to_text(pcs->result, NULL);
> 
> -	if (found_one) {
> -	    D(("user [%s] matched - caps are [%s]", user, cap_string));
> -	    break;
> -	}
> +	syslog(LOG_DEBUG, "configuration for user %s is %s",
> +	       pcs->user, tmp);
> +	cap_free(tmp);
>      }
> +    rc = PAM_SUCCESS;
> 
> +  out:
>      fclose(cap_file);
> 
> -    memset(buffer, 0, CAP_FILE_BUFFER_SIZE);
> -
> -    return cap_string;
> +    return rc;
>  }
> 
>  /*
>   * Set capabilities for current process to match the current
>   * permitted+executable sets combined with the configured inheritable
> - * set.
> + * and bounding set.
>   */
> 
> -static int set_capabilities(struct pam_cap_s *cs)
> +static int set_capabilities(struct pam_cap_s *pcs)
>  {
> -    cap_t cap_s;
> -    ssize_t length = 0;
> -    char *conf_icaps;
> -    char *proc_epcaps;
> -    char *combined_caps;
> -    int ok = 0;
> -
> -    cap_s = cap_get_proc();
> -    if (cap_s == NULL) {
> -	D(("your kernel is capability challenged - upgrade: %s",
> -	   strerror(errno)));
> -	return 0;
> -    }
> -
> -    conf_icaps =
> -	read_capabilities_for_user(cs->user,
> -				   cs->conf_filename
> -				   ? cs->conf_filename:USER_CAP_FILE );
> -    if (conf_icaps == NULL) {
> -	D(("no capabilities found for user [%s]", cs->user));
> -	goto cleanup_cap_s;
> -    }
> -
> -    proc_epcaps = cap_to_text(cap_s, &length);
> -    if (proc_epcaps == NULL) {
> -	D(("unable to convert process capabilities to text"));
> -	goto cleanup_icaps;
> -    }
> -
> -    /*
> -     * This is a pretty inefficient way to combine
> -     * capabilities. However, it seems to be the most straightforward
> -     * one, given the limitations of the POSIX.1e draft spec. The spec
> -     * is optimized for applications that know the capabilities they
> -     * want to manipulate at compile time.
> -     */
> -
> -    combined_caps = malloc(1+strlen(CAP_COMBINED_FORMAT)
> -			   +strlen(proc_epcaps)+strlen(conf_icaps));
> -    if (combined_caps == NULL) {
> -	D(("unable to combine capabilities into one string - no memory"));
> -	goto cleanup_epcaps;
> -    }
> -
> -    if (!strcmp(conf_icaps, "none")) {
> -	sprintf(combined_caps, CAP_DROP_ALL, proc_epcaps);
> -    } else if (!strcmp(conf_icaps, "all")) {
> -	/* no change */
> -	sprintf(combined_caps, "%s", proc_epcaps);
> -    } else {
> -	sprintf(combined_caps, CAP_COMBINED_FORMAT, proc_epcaps, conf_icaps);
> -    }
> -    D(("combined_caps=[%s]", combined_caps));
> -
> -    cap_free(cap_s);
> -    cap_s = cap_from_text(combined_caps);
> -    _pam_overwrite(combined_caps);
> -    _pam_drop(combined_caps);
> -
> -#ifdef DEBUG
> -    {
> -        char *temp = cap_to_text(cap_s, NULL);
> -	D(("abbreviated caps for process will be [%s]", temp));
> -	cap_free(temp);
> -    }
> -#endif /* DEBUG */
> -
> -    if (cap_s == NULL) {
> -	D(("no capabilies to set"));
> -    } else if (cap_set_proc(cap_s) == 0) {
> -	D(("capabilities were set correctly"));
> -	ok = 1;
> -    } else {
> -	D(("failed to set specified capabilities: %s", strerror(errno)));
> -    }
> -
> -cleanup_epcaps:
> -    cap_free(proc_epcaps);
> -
> -cleanup_icaps:
> -    _pam_overwrite(conf_icaps);
> -    _pam_drop(conf_icaps);
> +    cap_value_t value;
> +    cap_flag_value_t code;
> +    int rc = -1;	/* PAM_(AUTH|CRED|SESSION)_ERR */
> +
> +    /* set inheritable capability set */
> +    if (pcs->do_set_inh) {
> +	cap_t cap_s = cap_get_proc();
> +	if (!cap_s) {
> +	    syslog(LOG_ERR, "your kernel is capability challenged - upgrade: %s",
> +		   strerror(errno));
> +	    goto out;
> +	}
> +	for (value=0; ;value++) {
> +	    if (cap_get_flag(pcs->result, value, CAP_INHERITABLE, &code))
> +		break;
> +	    cap_set_flag(cap_s, CAP_INHERITABLE, 1, &value, code);
> +	}
> +	if (cap_set_proc(cap_s) < 0) {
> +	    if (errno == EPERM)
> +		rc = PAM_PERM_DENIED;
> +	    syslog(LOG_ERR, "unable to set inheritable capabilities (%s)",
> +		   strerror(errno));
> +	    cap_free(cap_s);
> +	    goto out;
> +	}
> +	if (pcs->debug) {
> +	    char *tmp = cap_to_text(cap_s, NULL);
> 
> -cleanup_cap_s:
> -    if (cap_s) {
> +	    syslog(LOG_DEBUG, "user %s new capabilities: %s",
> +		   pcs->user, tmp);
> +	    cap_free(tmp);
> +	}
>  	cap_free(cap_s);
> -	cap_s = NULL;
>      }
> +    /* drop capability bounding set */
> +    if (pcs->do_set_bset) {
> +	for (value=0; ;value++) {
> +	    if (cap_get_flag(pcs->result, value, CAP_PERMITTED, &code))
> +		break;
> +	    if (code == CAP_SET) {
> +		if (prctl(PR_CAPBSET_DROP, value) < 0) {
> +		    syslog(LOG_ERR, "unable to drop capability b-set %u (%s)",
> +			   value, strerror(errno));
> +		    goto out;
> +		}
> +		if (pcs->debug)
> +		    syslog(LOG_DEBUG, "%s drops capability %s from bounding set",
> +			   pcs->user, _cap_names[value]);
> +	    }
> +	}
> +    }
> +    rc = PAM_SUCCESS;
> 
> -    return ok;
> -}
> -
> -/* log errors */
> -
> -static void _pam_log(int err, const char *format, ...)
> -{
> -    va_list args;
> +  out:
> +    cap_free(pcs->result);
> 
> -    va_start(args, format);
> -    openlog("pam_cap", LOG_CONS|LOG_PID, LOG_AUTH);
> -    vsyslog(err, format, args);
> -    va_end(args);
> -    closelog();
> +    return rc;
>  }
> 
> -static void parse_args(int argc, const char **argv, struct pam_cap_s *pcs)
> +static int init_pam_cap(pam_handle_t *pamh, int argc, const char **argv,
> +			struct pam_cap_s *pcs)
>  {
> -    int ctrl=0;
> +    int ctrl, rc;
> +
> +    /* Initialization */
> +    memset(pcs, 0, sizeof(struct pam_cap_s));
> +    pcs->conf_filename = USER_CAP_FILE;
> +    rc = pam_get_user(pamh, &pcs->user, NULL);
> +    if (rc == PAM_CONV_AGAIN) {
> +	syslog(LOG_INFO, "user conversation is not available yet");
> +	return PAM_INCOMPLETE;
> +    }
> +    if (rc != PAM_SUCCESS) {
> +	syslog(LOG_INFO, "pam_get_user failed: %s", pam_strerror(pamh, rc));
> +	return -1;
> +    }
> 
>      /* step through arguments */
>      for (ctrl=0; argc-- > 0; ++argv) {
> -
>  	if (!strcmp(*argv, "debug")) {
>  	    pcs->debug = 1;
>  	} else if (!strcmp(*argv, "config=")) {
>  	    pcs->conf_filename = strlen("config=") + *argv;
>  	} else {
> -	    _pam_log(LOG_ERR, "unknown option; %s", *argv);
> +	    syslog(LOG_ERR, "unknown option: %s", *argv);
> +	    return -1;
>  	}
> +    }
> +    return PAM_SUCCESS;
> +}
> 
> +static void cleanup_pam_cap(pam_handle_t *pamh, void *data, int error_status)
> +{
> +    struct pam_cap_s *pcs = (struct pam_cap_s *) data;
> +
> +    if (pcs) {
> +	if (pcs->result)
> +	    cap_free(pcs->result);
> +	free(pcs);
>      }
>  }
> 
>  int pam_sm_authenticate(pam_handle_t *pamh, int flags,
>  			int argc, const char **argv)
>  {
> -    int retval;
> -    struct pam_cap_s pcs;
> -    char *conf_icaps;
> +    struct pam_cap_s *pcs = NULL;
> +    int rc = PAM_BUF_ERR;
> 
> -    memset(&pcs, 0, sizeof(pcs));
> +    openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTHPRIV);
> 
> -    parse_args(argc, argv, &pcs);
> +    pcs = malloc(sizeof(struct pam_cap_s));
> +    if (!pcs)
> +	goto error;
> 
> -    retval = pam_get_user(pamh, &pcs.user, NULL);
> +    rc = init_pam_cap(pamh, argc, argv, pcs);
> +    if (rc != PAM_SUCCESS)
> +	goto error;
> 
> -    if (retval == PAM_CONV_AGAIN) {
> -	D(("user conversation is not available yet"));
> -	memset(&pcs, 0, sizeof(pcs));
> -	return PAM_INCOMPLETE;
> -    }
> +    rc = read_capabilities_for_user(pcs);
> +    if (rc != PAM_SUCCESS)
> +	goto error;
> 
> -    if (retval != PAM_SUCCESS) {
> -	D(("pam_get_user failed: %s", pam_strerror(pamh, retval)));
> -	memset(&pcs, 0, sizeof(pcs));
> -	return PAM_AUTH_ERR;
> +    rc = pam_set_data(pamh, MODULE_NAME, pcs, cleanup_pam_cap);
> +    if (rc == PAM_SUCCESS) {
> +	/* OK, pam_sm_setcred() will be called next */
> +	closelog();
> +	return rc;
>      }
> 
> -    conf_icaps =
> -	read_capabilities_for_user(pcs.user,
> -				   pcs.conf_filename
> -				   ? pcs.conf_filename:USER_CAP_FILE );
> -
> -    memset(&pcs, 0, sizeof(pcs));
> -
> -    if (conf_icaps) {
> -	D(("it appears that there are capabilities for this user [%s]",
> -	   conf_icaps));
> +  error:
> +    cleanup_pam_cap(pamh, pcs, rc);
> +    closelog();
> +    return rc < 0 ? PAM_AUTH_ERR : rc;
> +}
> 
> -	/* We could also store this as a pam_[gs]et_data item for use
> -	   by the setcred call to follow. As it is, there is a small
> -	   race associated with a redundant read. Oh well, if you
> -	   care, send me a patch.. */
> +int pam_sm_setcred(pam_handle_t *pamh, int flags,
> +		   int argc, const char **argv)
> +{
> +    struct pam_cap_s *pcs = NULL;
> +    int rc = PAM_IGNORE;
> 
> -	_pam_overwrite(conf_icaps);
> -	_pam_drop(conf_icaps);
> +    openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTHPRIV);
> 
> -	return PAM_SUCCESS;
> +    if (!(flags & PAM_ESTABLISH_CRED))
> +	goto out;
> 
> -    } else {
> +    rc = pam_get_data(pamh, MODULE_NAME, (void *)&pcs);
> +    if (rc != PAM_SUCCESS)
> +	return rc;
> 
> -	D(("there are no capabilities restrctions on this user"));
> -	return PAM_IGNORE;
> +    rc = set_capabilities(pcs);
> 
> -    }
> +  out:
> +    closelog();
> +    return rc < 0 ? PAM_CRED_ERR : rc;
>  }
> 
> -int pam_sm_setcred(pam_handle_t *pamh, int flags,
> -		   int argc, const char **argv)
> +int pam_sm_open_session(pam_handle_t *pamh, int flags,
> +			int argc, const char **argv)
>  {
> -    int retval;
>      struct pam_cap_s pcs;
> +    int rc;
> 
> -    if (!(flags & PAM_ESTABLISH_CRED)) {
> -	D(("we don't handle much in the way of credentials"));
> -	return PAM_IGNORE;
> -    }
> +    openlog(MODULE_NAME, LOG_CONS|LOG_PID, LOG_AUTHPRIV);
> 
> -    memset(&pcs, 0, sizeof(pcs));
> +    rc = init_pam_cap(pamh, argc, argv, &pcs);
> +    if (rc != PAM_SUCCESS)
> +	goto out;
> 
> -    parse_args(argc, argv, &pcs);
> +    rc = read_capabilities_for_user(&pcs);
> +    if (rc != PAM_SUCCESS)
> +	goto out;
> 
> -    retval = pam_get_item(pamh, PAM_USER, (const void **)&pcs.user);
> -    if ((retval != PAM_SUCCESS) || (pcs.user == NULL) || !(pcs.user[0])) {
> +    rc = set_capabilities(&pcs);
> 
> -	D(("user's name is not set"));
> -	return PAM_AUTH_ERR;
> +    if (rc == PAM_SUCCESS) {
> +	rc = set_capabilities(&pcs);
> +	if (pcs.result)
> +	    cap_free(pcs.result);
>      }
> 
> -    retval = set_capabilities(&pcs);
> +  out:
> +    if (pcs.result)
> +	cap_free(pcs.result);
> +    closelog();
> 
> -    memset(&pcs, 0, sizeof(pcs));
> +    return rc < 0 ? PAM_SESSION_ERR : rc;
> +}
> 
> -    return (retval ? PAM_SUCCESS:PAM_IGNORE );
> +int pam_sm_close_session(pam_handle_t *pamh, int flags,
> +			 int argc, const char **argv)
> +{
> +    return PAM_SUCCESS; /* do nothing */
>  }
> 
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFHWOSqmwytjiwfWMwRAnSIAJ0ea1HisHTLBfeApmdoHx+aSRbQ9wCbBC9C
I8mLshEVleoPG9OkJVUHTo0=
=WZO6
-----END PGP SIGNATURE-----
--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to [email protected]
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

[Index of Archives]     [Kernel Newbies]     [Netfilter]     [Bugtraq]     [Photo]     [Stuff]     [Gimp]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Video 4 Linux]     [Linux for the blind]     [Linux Resources]
  Powered by Linux