X-Git-Url: https://git.hcoop.net/hcoop/debian/libnss-afs.git/blobdiff_plain/03b6b479897df2f195120622e69f87aede855589..be93aa7b0ef9b5b58a15e03a0bf47a8b5cf8e9fc:/nss_afs.c diff --git a/nss_afs.c b/nss_afs.c index 6004e74..9c9740c 100644 --- a/nss_afs.c +++ b/nss_afs.c @@ -1,17 +1,37 @@ -/* - * libnss_afs.c +/***************************************************************************** + * libnss-afs (nss_afs.c) * * Copyright 2008, licensed under GNU Library General Public License (LGPL) * see COPYING file for details * + * by Adam Megacz * derived from Frank Burkhardt's libnss_ptdb, * which was derived from Todd M. Lewis' libnss_pts - */ + *****************************************************************************/ /* - * if you are reading this code for the first time, start at the - * bottom and work your way upwards + * If you are reading this code for the first time, read the rest of + * this comment block, then start at the bottom of the file and work + * your way upwards. + * + * All functions which return an int use zero to signal success -- + * except cpstr(), which returns zero on *failure*. This should be + * fixed. + * + * A note about memory allocation: + * + * NSS plugins generally ought to work without attempting to call + * malloc() (which may fail). Therefore, glibc allocates a buffer + * before calling NSS library functions, and passes that buffer to + * the NSS library; library functions store their results in the + * buffer and return pointers into that buffer. + * + * The convention used throughout this library is to pass around a + * char** which points to a pointer to the first unused byte in the + * provided buffer, and a size_t* which points to an int indicating + * how many bytes are left between the char** and the end of the + * available region. */ #include @@ -32,6 +52,7 @@ #include #include #include +#include #include #include #include @@ -44,46 +65,53 @@ #include #include -#define HOMEDIR_AUTO 0 +#define HOMEDIR_AUTO 0 #define HOMEDIR_ADMINLINK 1 -#define HOMEDIR_PREFIX 2 -#define SHELL_BASH 0 -#define SHELL_ADMINLINK 1 -#define SHELL_USERLINK 2 +#define HOMEDIR_PREFIX 2 +#define SHELL_BASH 0 +#define SHELL_ADMINLINK 1 +#define SHELL_USERLINK 2 #define AFS_MAGIC_ANONYMOUS_USERID 32766 -#define MIN_PAG_GID 0x41000000L -#define MAX_PAG_GID 0x41FFFFFFL +#define MIN_PAG_GID 0x41000000L +#define MAX_PAG_GID 0x41FFFFFFL +#define MIN_OLDPAG_GID 0x3f00 +#define MAX_OLDPAG_GID 0xff00 -static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; +#define MAXCELLNAMELEN 256 +#define MAXUSERNAMELEN 256 -extern int cpstr( char *str, char **buf, size_t *buflen); +static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; extern struct ubik_client *pruclient; int afs_initialized = 0; -char cellname[256]; -char homedir_prefix[300]; +char cellname[MAXCELLNAMELEN]; +char homedir_prefix[MAXPATHLEN]; +char cell_root[MAXPATHLEN]; int homedir_prefix_len=0; char homedirs_method=0; char shells_method=0; +/** + * The cpstr() function copies a null-terminated string from str* + * (the first argument) into buf and updates both buf and buflen. If + * the string would overflow the buffer, no action is taken. The + * number of bytes copied is returned (zero indicates failure). + */ int cpstr( char *str, char **buf, size_t *buflen) { - int len = strlen(str); - if ( len >= *buflen-1 ) return 0; - strcpy(*buf,str); - *buflen -= len + 1; - *buf += len + 1; - return len; + int len = strlen(str); + if ( len >= *buflen-1 ) return 0; + strcpy(*buf,str); + *buflen -= len + 1; + *buf += len + 1; + return len; } - -/* +/** * Look up the name corresponding to uid, store in buffer. - * - * returns NSS_STATUS_SUCCESS, NSS_STATUS_NOTFOUND, or NSS_STATUS_UNAVAIL */ -int ptsid2name(int uid, char **buffer, int *buflen) { +enum nss_status ptsid2name(int uid, char **buffer, int *buflen) { int ret, i; idlist lid; namelist lnames; @@ -103,6 +131,7 @@ int ptsid2name(int uid, char **buffer, int *buflen) { lnames.namelist_len = 0; if (ubik_Call(PR_IDToName,pruclient,0,&lid,&lnames) != PRSUCCESS) { + perror("ubik_Call() in ptsid2name() failed\n"); pthread_mutex_unlock(&mutex); return NSS_STATUS_UNAVAIL; } @@ -124,15 +153,14 @@ int ptsid2name(int uid, char **buffer, int *buflen) { return ret; } -/* - * Look up the uid corresponding to name, stores it in *uid. - * - * returns NSS_STATUS_SUCCESS, NSS_STATUS_NOTFOUND, or NSS_STATUS_UNAVAIL +/** + * Look up the uid corresponding to name in ptserver. */ -int ptsname2id(char *name, uid_t* uid) { +enum nss_status ptsname2id(char *name, uid_t* uid) { int res; idlist lid; namelist lnames; + char uname[MAXUSERNAMELEN]; init_afs(); @@ -145,10 +173,13 @@ int ptsname2id(char *name, uid_t* uid) { lid.idlist_val = 0; lid.idlist_len = 0; - lnames.namelist_val = (prname*)name; + lnames.namelist_val = (prname*)uname; + // apparently ubik expects to be able to modify this? + strncpy(uname, name, MAXUSERNAMELEN); lnames.namelist_len = 1; if (ubik_Call(PR_NameToID,pruclient,0,&lnames,&lid) != PRSUCCESS) { + perror("ubik_Call() in ptsname2id() failed\n"); pthread_mutex_unlock(&mutex); return NSS_STATUS_UNAVAIL; } @@ -160,23 +191,32 @@ int ptsname2id(char *name, uid_t* uid) { return NSS_STATUS_SUCCESS; } -/* - * returns zero on success +/** + * Initialize the library; returns zero on success */ int init_afs() { FILE *thiscell; int len; + struct stat statbuf; - if (afs_initialized) return 0; - + if (afs_initialized) { + /* wait until /afs/@cell/ appears as a proxy for "the network is up" */ + if (stat(cell_root, &statbuf)) return -1; + return 0; + } + if (pthread_mutex_lock(&mutex)) return -1; do { homedirs_method=HOMEDIR_PREFIX; shells_method=SHELL_USERLINK; - - thiscell=fopen("/etc/openafs/ThisCell","r"); + + len = snprintf(cellname, MAXCELLNAMELEN, + "%s/ThisCell", AFSDIR_CLIENT_ETC_DIRPATH); + if (len < 0 || len >= MAXCELLNAMELEN) return -1; + + thiscell=fopen(cellname,"r"); if (thiscell == NULL) break; - len=fread(cellname,1,256,thiscell); + len=fread(cellname,1,MAXCELLNAMELEN,thiscell); if (!feof(thiscell)) { // Cellname too long fclose(thiscell); @@ -185,12 +225,24 @@ int init_afs() { break; } fclose(thiscell); + if (cellname[len-1] == '\n') len--; cellname[len]='\0'; + + /* wait until /afs/@cell/ appears as a proxy for "the network is up" */ + sprintf(cell_root,"/afs/%s/",cellname); + if (stat(cell_root, &statbuf)) break; + sprintf(homedir_prefix,"/afs/%s/user/",cellname); homedir_prefix_len=strlen(homedir_prefix); - - if (pr_Initialize(0L,AFSDIR_CLIENT_ETC_DIRPATH, 0)) break; + + /* time out requests after 5 seconds to avoid hanging things */ + rx_SetRxDeadTime(5); + + if (pr_Initialize(0L,AFSDIR_CLIENT_ETC_DIRPATH, 0)) { + perror("pr_Initialize() failed\n"); + break; + } afs_initialized = 1; pthread_mutex_unlock(&mutex); @@ -202,12 +254,9 @@ int init_afs() { } -/* - result=get_homedir(char *name,char **buffer,size_t *buflen) - Writes the guessed Homedirectory of a given user 'name' into - '*buffer', increasing *buflen accordingly. - result == 1, only if the buffer was big enough. -*/ +/** + * Retrieves the homedir for a given user; returns 0 on success. + */ int get_homedir(char *name, char **buffer, size_t *buflen) { char buf[256]; int temp; @@ -244,6 +293,9 @@ int get_homedir(char *name, char **buffer, size_t *buflen) { return 0; } +/** + * Retrieves the shell for a given user; returns 0 on success. + */ int get_shell(char *name, char **buffer, size_t *buflen) { char buf[256]; int temp; @@ -254,46 +306,48 @@ int get_shell(char *name, char **buffer, size_t *buflen) { switch (shells_method) { case SHELL_BASH: - if (!cpstr("/bin/bash",buffer, buflen)) return -1; break; case SHELL_ADMINLINK: - if ( snprintf(buf,256,"/afs/%s/admin/shells/%s",cellname,name) > 0 ) { - temp=readlink(buf,*buffer,*buflen); - if ( temp > -1) { - b[temp]=0; - *buflen = *buflen - temp - 1; - return -1; - } - } - if (! cpstr("/bin/bash",buffer,buflen) ) - return -1; - break; + if (snprintf(buf,256,"/afs/%s/admin/shells/%s",cellname,name)<=0) break; + temp = readlink(buf,*buffer,*buflen); + if (temp < 0) break; + b[temp]=0; + *buflen = *buflen - temp - 1; + return 0; case SHELL_USERLINK: - if (get_homedir(name, &bufx, &bufxlen)) return -1; - strncpy(buf+strlen(buf),"/.loginshell",bufxlen); - temp=readlink(buf,*buffer,*buflen); - if ( temp > -1) { - b[temp]=0; - *buflen = *buflen - temp - 1; - return -1; - } - if (! cpstr("/bin/bash",buffer,buflen) ) - return -1; - break; + if (get_homedir(name, &bufx, &bufxlen)) break; + if (strncpy(buf+strlen(buf),"/.loginshell",bufxlen)<=0) break; + temp = readlink(buf,*buffer,*buflen); + if (temp < 0) break; + b[temp]=0; + *buflen = *buflen - temp - 1; + return 0; } + if (! cpstr("/bin/bash",buffer,buflen) ) + return -1; return 0; } -/** - * this function is invoked by glibc to resolve the name of a numeric groupid +/* + * This function is exported; glibc will invoke it in order to find + * the name and list of members of a group specified by a numerical + * groupid. */ -enum nss_status _nss_afs_getgrgid_r (gid_t gid, struct group *result, - char *buffer, size_t buflen, int *errnop) { +enum nss_status _nss_afs_getgrgid_r (gid_t gid, + struct group *result, + char *buffer, + size_t buflen, + int *errnop) { int length; - if ( gid < MIN_PAG_GID || gid > MAX_PAG_GID) { + int showgid = 0; + if (gid >= MIN_PAG_GID && gid <= MAX_PAG_GID) { + showgid = gid-MIN_PAG_GID; + } else if (gid >= MIN_OLDPAG_GID && gid <= MAX_OLDPAG_GID) { + showgid = gid-MIN_OLDPAG_GID; + } else { *errnop=ENOENT; return NSS_STATUS_NOTFOUND; } @@ -301,7 +355,7 @@ enum nss_status _nss_afs_getgrgid_r (gid_t gid, struct group *result, result->gr_gid=gid; result->gr_name=buffer; - length=snprintf(buffer,buflen,"AfsPag-%x",gid-MIN_PAG_GID); + length=snprintf(buffer,buflen,"AfsPag-%x",showgid); if (length < 0) break; length += 1; @@ -324,36 +378,26 @@ enum nss_status _nss_afs_getgrgid_r (gid_t gid, struct group *result, return NSS_STATUS_UNAVAIL; } -/* - This is a the ptdb-getpwuid-function. -*/ -enum nss_status _nss_afs_getpwuid_r (uid_t uid, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop) { - int temp; - - if (init_afs()) return NSS_STATUS_UNAVAIL; - - result_buf->pw_name = buffer; - temp = ptsid2name( uid, &buffer, &buflen); - if (temp != NSS_STATUS_SUCCESS) { - *errnop = ENOENT; - return temp; - } - -#ifdef LIMIT_USERNAME_CHARS - if ( strlen(result_buf->pw_name) > LIMIT_USERNAME_CHARS ) { - result_buf->pw_name[LIMIT_USERNAME_CHARS] = '\0'; - buflen = buflen + ( buffer - &result_buf->pw_name[LIMIT_USERNAME_CHARS+1] ); - buffer = &result_buf->pw_name[LIMIT_USERNAME_CHARS+1]; - } -#endif - +/** + * A helper function to fill in the fields of "struct passwd"; used by + * both _nss_afs_getpwuid_r() and _nss_afs_getpwnam_r(). + */ +enum nss_status fill_result_buf(uid_t uid, + char* name, + struct passwd *result_buf, + char *buffer, + size_t buflen, + int *errnop) { + result_buf->pw_name = name; do { /* set the password to "x" */ result_buf->pw_passwd = buffer; if ( ! cpstr("x",&buffer, &buflen) ) break; + /* the uid and gid are both the uid passed in */ result_buf->pw_uid = uid; result_buf->pw_gid = 65534; + /* make the gecos the same as the PTS name */ result_buf->pw_gecos = buffer; if ( ! cpstr(result_buf->pw_name, &buffer, &buflen ) ) break; @@ -366,6 +410,14 @@ enum nss_status _nss_afs_getpwuid_r (uid_t uid, struct passwd *result_buf, char result_buf->pw_shell = buffer; if ( get_shell(result_buf->pw_name,&buffer,&buflen) ) break; +#ifdef LIMIT_USERNAME_CHARS + if ( strlen(result_buf->pw_name) > LIMIT_USERNAME_CHARS ) { + result_buf->pw_name[LIMIT_USERNAME_CHARS] = '\0'; + buflen = buflen + ( buffer - &result_buf->pw_name[LIMIT_USERNAME_CHARS+1] ); + buffer = &result_buf->pw_name[LIMIT_USERNAME_CHARS+1]; + } +#endif + *errnop = errno; return NSS_STATUS_SUCCESS; } while(0); @@ -375,10 +427,41 @@ enum nss_status _nss_afs_getpwuid_r (uid_t uid, struct passwd *result_buf, char } -/* - * This is the ptdb-getpwnam-function. +/** + * This function is exported; glibc will invoke it in order to gather + * the user information (userid, homedir, shell) associated with a + * numerical userid. + */ +enum nss_status _nss_afs_getpwuid_r (uid_t uid, + struct passwd *result_buf, + char *buffer, + size_t buflen, + int *errnop) { + int temp; + char* name; + + if (init_afs()) return NSS_STATUS_UNAVAIL; + + name = buffer; + temp = ptsid2name( uid, &buffer, &buflen); + if (temp != NSS_STATUS_SUCCESS) { + *errnop = ENOENT; + return temp; + } + + return fill_result_buf(uid, name, result_buf, buffer, buflen, errnop); +} + +/** + * This function is exported; glibc will invoke it in order to gather + * the user information (userid, homedir, shell) associated with a + * username. */ -enum nss_status _nss_afs_getpwnam_r (char *name, struct passwd *result_buf, char *buffer, size_t buflen, int *errnop) { +enum nss_status _nss_afs_getpwnam_r (char *name, + struct passwd *result_buf, + char *buffer, + size_t buflen, + int *errnop) { uid_t uid; int temp; @@ -390,7 +473,6 @@ enum nss_status _nss_afs_getpwnam_r (char *name, struct passwd *result_buf, char return temp; } - // This causes an additional PTDB-lookup and should be removed in the future - return _nss_afs_getpwuid_r (uid, result_buf,buffer,buflen, errnop); + return fill_result_buf(uid, name, result_buf, buffer, buflen, errnop); }