Re: [mituc@xxxxxxxxxxxxxx: pam limits drops privileges]

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

 



On Wed, Sep 12, 2001 at 02:32:27PM -0500, Steve Langasek wrote:
> Is the following ok for a first implementation, or do you have more ambitious
> plans?  I believe you mentioned providing a wrapper for systems that don't
> have getpwnam_r(), but I'm personally quite content with this.
[snip]
> #if HAVE_GETPWNAM_R
>     getpwnam_r(name, pwd, buf, sizeof(buf), &pwd);
> #else
>     pwd = getpwnam(name);
> #endif

I tried this route, and discovered that if getpwnam_r fails, you have
no guarantees about the value of the parameters you passed in (pwd
may be unchanged, or it may point to the struct passwd, or to some
other location), so it's probably better to do this:

	if(getpwnam_r(name, pwd, buf, sizeof(buf), &pwd) != 0) {
		pwd = NULL;
	}

But Thorsten's correct that this doesn't catch ERANGE problems which
are likely to crop up when you have really big groups.  I was toying
with the attached piece of untested code (it's a bit more complicated
than it might need to be, because I'm trying to elimitate duplicate
code paths in the main querying function, and it allocates a separate
PAM data item for use with each query).  Any comments about it?

Nalin
#include <sys/types.h>
#include <errno.h>
#include <pwd.h>
#include <grp.h>
#include <stdlib.h>
#include <string.h>
#include <security/pam_modules.h>

#if defined(HAVE_GETPWNAM_R) || defined(HAVE_GETPWUID_R) || defined(HAVE_GETGRNAM_R) || defined(HAVE_GETGRGID_R)

#define CHUNK_SIZE 1024

struct _linuxpam_passwd {
	struct passwd pwd;
	struct group grp;
	size_t buffer_length;
	char *buffer;
};

/* The cleanup function which frees a query structure at cleanup-time. */
static void
free_buffer(pam_handle_t *pamh, void *buffer, int status)
{
	struct _linuxpam_passwd *passwd;
	passwd = buffer;
	if(passwd) {
		if((passwd->buffer_length > 0) && (passwd->buffer != NULL)) {
			free(passwd->buffer);
			passwd->buffer = NULL;
			passwd->buffer_length = 0;
		}
		memset(passwd, 0, sizeof(struct _linuxpam_passwd));
		free(passwd);
	}
}

enum ltype {
	pwnam,
	pwuid,
	grnam,
	grgid,
};

/* Warning: big-time not portable to other PAM implementations. */
static void *
_linuxpam_getXXXXX(pam_handle_t *pamh, const char *dataname, enum ltype ltype,
		   const char *name, long id)
{
	struct _linuxpam_passwd *passwd = NULL;
	struct passwd *pwd = NULL;
	struct group *grp = NULL;
	int i;

	/* Check for a previously-used structure for this query.  If there
	 * isn't one, allocate one. */
	if((pam_get_data(pamh, dataname, (const void**) &passwd) != PAM_SUCCESS) ||
	   (passwd == NULL)) {
		passwd = malloc(sizeof(struct _linuxpam_passwd));
		if(passwd == NULL) {
			return NULL;
		}
		memset(&passwd, 0, sizeof(struct _linuxpam_passwd));
		passwd->buffer_length = CHUNK_SIZE;
		passwd->buffer = malloc(passwd->buffer_length);
		if(passwd->buffer == NULL) {
			/* We're screwed; give up. */
			free(passwd);
			return NULL;
		}
		if(pam_set_data(pamh, dataname, passwd, free_buffer) != PAM_SUCCESS) {
			/* Couldn't set, free the memory and give up. */
			free(passwd->buffer);
			free(passwd);
			return NULL;
		}
	}
	while(1) {
		switch(ltype) {
			case pwnam:
				i = getpwnam_r(name,
					       &passwd->pwd,
					       passwd->buffer,
					       passwd->buffer_length,
					       &pwd);
				if(i == 0) {
					return pwd;
				}
				break;
			case grnam:
				i = getgrnam_r(name,
					       &passwd->grp,
					       passwd->buffer,
					       passwd->buffer_length,
					       &grp);
				if(i == 0) {
					return grp;
				}
				break;
			case pwuid:
				i = getpwuid_r(id,
					       &passwd->pwd,
					       passwd->buffer,
					       passwd->buffer_length,
					       &pwd);
				if(i == 0) {
					return pwd;
				}
				break;
			case grgid:
				i = getgrgid_r(id,
					       &passwd->grp,
					       passwd->buffer,
					       passwd->buffer_length,
					       &grp);
				if(i == 0) {
					return grp;
				}
				break;
			default:
				return NULL;
		}
		pwd = NULL;
		grp = NULL;
		if(errno == ERANGE) {
			/* Not enough buffer space -- allocate a bigger one and
			 * try again. */
			char *tmp;
			tmp = malloc(passwd->buffer_length + CHUNK_SIZE);
			if(tmp == NULL) {
				return NULL;
			}
			free(passwd->buffer);
			passwd->buffer = tmp;
			passwd->buffer_length += CHUNK_SIZE;
		} else {
			/* Didn't find anything, or some other error. */
			return NULL;
		}
	}
}
#endif

/* Wrappers which hand off specific queries to the above function. */
struct passwd *
_linuxpam_getpwnam(pam_handle_t *pamh, const char *dataname, const char *login)
{
#ifdef HAVE_GETPWNAM_R
	return _linuxpam_getXXXXX(pamh, dataname, pwnam, login, 0);
#else
	return getpwnam(login);
#endif
}

struct passwd *
_linuxpam_getpwuid(pam_handle_t *pamh, const char *dataname, uid_t uid)
{
#ifdef HAVE_GETPWUID_R
	return _linuxpam_getXXXXX(pamh, dataname, pwuid, NULL, uid);
#else
	return getpwuid(uid);
#endif
}

struct group *
_linuxpam_getgrnam(pam_handle_t *pamh, const char *dataname, const char *group)
{
#ifdef HAVE_GETGRNAM_R
	return _linuxpam_getXXXXX(pamh, dataname, grnam, group, 0);
#else
	return getgrnam(group);
#endif
}

struct group *
_linuxpam_getgrgid(pam_handle_t *pamh, const char *dataname, gid_t gid)
{
#ifdef HAVE_GETGRGID_R
	return _linuxpam_getXXXXX(pamh, dataname, grgid, NULL, gid);
#else
	return getgrgid(gid);
#endif
}

[Index of Archives]     [Fedora Users]     [Kernel]     [Red Hat Install]     [Linux for the blind]     [Gimp]

  Powered by Linux