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 }