/************************************************************************** nss_ncp.c NSS module for NDS Copyright (C) 2002 Patrick Pollet This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Revision history: 1.00 2003, January 06 Patrick Pollet initial release 1.01 2003, January 08 Patrick Pollet added conf structure and control group added optional fallback UID and GID if none found in NDS (default is to skip user,group) 1.02 2003, January 09 Patrick Pollet added initgroups 1.03 2003, January 10 Patrick Pollet fixed bug in nds_user_info2 (bad structure received by nds_user_location2) 1.04 2003, January 11 Patrick Pollet fixed setting ndsXXX=NULL trees in case of errors in _nss_ncp_setxxent() made always NAME_CONTEXT=[Root] in CreateContextAndConn calling NWCCloseIteration only it some errors has occured in the search 1.05 2003, January 15 Patrick Pollet -Avoid multiple reading of conf file by removing recursive calls in nss_ncp_getxxent_r in case a entry has no Unix infos in NDS (replaced by a goto nextuser) -Added missing free_nw_xxx_info when leaving nss_ncp_getxxent_r (fixed memory leaks) -Added testing for failure in allocating tree structure in nss_ncp_setxxent_r -if (id !=(uid_t)-1) and not if (id) in getentxx if we search by UID !!!! -getentbyxx give a warning in syslog if more that one entry match the name or id search criteria 1.06 2003, January 16 Patrick Pollet -implemented reading of configuration file in /etc/nss_ncp.conf -in case of fatal errors, force log in syslog by using calls to traceForce ( previously fatal errors were only reported in debug mode, since the syslog file is not opened in normal mode) 1.07 2003, January 16 Patrick Pollet Speed up the search: 1)config informations are stored in the internal trees structures when calling nss_ncp_setxxent_r and freed by nss_ncp_endxxent_r so we don't read again config file at every call to nss_ncp_getxxent_r 2) group infos are really slow with big groups,so we added a flag doGroup in conf file to skip the search (easier that to edit /etc/nsswitch.conf AND restarting nscd daemon). ************************************************************************/ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "nss_ncp.h" #include "nss_cfgfile.h" // only if logfile has been opened by nss API functions (debug mode) static void trace (int debugMode,int err,const char * format,... ) { va_list args; if (debugMode) { va_start(args,format); vsyslog (err,format,args); va_end(args); } } // send a message to syslog even if log file not opened (debugMode is false) // needed for critical errors such a bad config file or ambiguous NDS search void traceForce (int debugMode,int err, const char * format,... ) { va_list args; if (!debugMode) openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); va_start(args,format); vsyslog (err,format,args); va_end(args); if (!debugMode) closelog(); } static int getnumber(int *val, const char **str) { const char *p = *str; char *z; if (!*p) return 1; if (*p == ',') { *str = p + 1; return 1; } *val = strtoul(p, &z, 0); if (p == z) return -1; if (*z == ',') z++; *str = z; return 0; } static char* copy_to_buffer (char ** buffer, size_t *buflen, char * value) { // copy the value to buffer , decrease buflen and return start of the value in buffer size_t len=strlen(value)+1; char * ptr=*buffer; if (len > *buflen) { return NULL; } memcpy(*buffer, value, len); *buflen -=len; (*buffer) +=len; return ptr; } /********** struct nss_ncp_conf { int debug; int useTree; // use Tree connection or server connection char * server; // name of server or tree char * startCtx; // start searching is this context (and below) char * ctrlGroup; // limit search to members of this NDS group for passwd and shadow gid_t defGid; // if no primary group found in NDS use this value char * defShell; // if no shell found in NDS use this value uid_t fallbackUid; // if no UID found in NDS use this one (-1= skip user, NFS_NOBODY= use this UID) gid_t fallbackGid; // if no GID found in NDS use this one (-1= skip group, NFS_NOBODY= use this GID) int doPassword; // if 0, will return immediarly NSS_STATUS_UNAVAILABLE even if ncp is listed in /etc/nsswitch.conf int doGroup; // if 0, will return immediarly NSS_STATUS_UNAVAILABLE even if ncp is listed in /etc/nsswitch.conf int doShadow; // if 0, will return immediarly NSS_STATUS_UNAVAILABLE even if ncp is listed in /etc/nsswitch.conf }; **************/ //one day we will read it from /etc/ncp_nss.conf /****************************************** internal structure for storing NDS user's password infos ***/ struct nw_user_info { char * cn; char * gecos; char * shell; char * dir; char * fullName; char * passwd; uid_t uid; gid_t gid; int qflag; }; static void init_nw_user_info (struct nw_user_info *ui,int qflag){ ui->cn=NULL; ui->gecos=NULL; ui->shell=NULL; ui->dir=NULL; ui->fullName=NULL; ui->passwd=NULL; ui->uid=(uid_t)-1; ui->gid=(gid_t)-1; ui->qflag= qflag; } static void free_nw_user_info (struct nw_user_info *ui){ #define FREEFIELD(x) do if (ui->x) {free(ui->x) ; ui->x=NULL;} while (0); FREEFIELD(cn); FREEFIELD(gecos); FREEFIELD(shell); FREEFIELD(dir); FREEFIELD(fullName); FREEFIELD(passwd); ui->uid=(uid_t)-1; ui->gid=(gid_t)-1; ui->qflag=0; #undef FREEFIELD } static int fix_nw_user_info (struct nw_user_info *ui, struct nss_ncp_conf* conf){ /* fill NDS missing attributes (home, shell, gid, gecos) with default values any user have a cn, and if uid was not found in NDS we consider that the corresponding Unix account is not activated */ if (ui->cn) { if (ui->uid== (uid_t)-1) ui->uid=conf->fallbackUid; if (ui->gid== (gid_t)-1) ui->gid=conf->defGid; if (!ui->gecos) { ui->gecos= ui->fullName ? strdup(ui->fullName):strdup(""); } ui->shell= ui->shell ? ui->shell: strdup(conf->defShell); ui->dir= ui->dir ? ui->dir: strdup(""); ui->passwd= ui->passwd ? ui->passwd: strdup("x"); // cannot read passwd from ND if (!ui->shell || !ui->dir || !ui->gecos || !ui->passwd) { traceForce(conf->debug,LOG_ERR, "not enough memory when fixing nw_user_info\n ",ui->cn); return 1; } }else { ui->uid = (uid_t)-1; // unable to read CN (NDS browse rights not set) , skip it ! } return 0; } // return found info to caller in the format expected by NSS that is: // filling the passwd structure as pointers to the passed buffer static enum nss_status nw_user_info_to_passwd(struct nw_user_info ui,struct passwd *pwd, char * buffer, size_t buflen, int * errnop, struct nss_ncp_conf *conf){ if (ui.uid != (uid_t)-1) { pwd->pw_uid=ui.uid; pwd->pw_gid=ui.gid; pwd->pw_name=copy_to_buffer (&buffer,&buflen,ui.cn); if (!pwd->pw_name) goto outnomem; pwd->pw_passwd=copy_to_buffer (&buffer,&buflen,ui.passwd); if (!pwd->pw_passwd) goto outnomem; pwd->pw_gecos=copy_to_buffer (&buffer,&buflen,ui.gecos); if (!pwd->pw_gecos) goto outnomem; pwd->pw_dir=copy_to_buffer (&buffer,&buflen,ui.dir); if (!pwd->pw_dir) goto outnomem; pwd->pw_shell=copy_to_buffer (&buffer,&buflen,ui.shell); if (!pwd->pw_shell) goto outnomem; *errnop=0; return NSS_STATUS_SUCCESS; outnomem: traceForce(conf->debug,LOG_ERR, "not enough memory when copying nw_user_info to passwd for %s\n ",ui.cn); *errnop=ERANGE; return NSS_STATUS_TRYAGAIN; } else { trace(conf->debug,LOG_NOTICE, "user %s has no Unix UID in NDS\n ", ui.cn); *errnop=ENOENT; return NSS_STATUS_NOTFOUND; } } static void print_nw_user_info (struct nw_user_info ui){ printf("%s:x:%d:%d:%s:%s:%s\n",ui.cn,ui.uid,ui.gid,ui.gecos,ui.dir,ui.shell); } static void print_passwd (struct passwd pwd){ printf("%s:%s:%d:%d:%s:%s:%s\n",pwd.pw_name,pwd.pw_passwd,pwd.pw_uid,pwd.pw_gid,pwd.pw_gecos,pwd.pw_dir,pwd.pw_shell); } /****************************************** internal structure for storing NDS group's infos ***/ struct nw_group_member { struct nw_group_member* next; char * member; }; struct nw_group_info { char * cn; char * alias; char * passwd; gid_t gid; struct nw_group_member * first; struct nw_group_member * last; int nbMembers; int qflag; }; static void init_nw_group_info (struct nw_group_info *gi,int qflag){ gi->cn=NULL; gi->alias=NULL; gi->passwd=NULL; gi->gid=(gid_t)-1; gi->qflag= qflag; gi->first=NULL; gi->last=NULL; gi->nbMembers=0; } static void free_nw_group_info (struct nw_group_info *gi){ struct nw_group_member* p; struct nw_group_member* bkp; #define FREEFIELD(x) do if (gi->x) {free(gi->x) ; gi->x=NULL;} while (0); FREEFIELD(cn); FREEFIELD(alias); FREEFIELD(passwd); gi->gid=(gid_t)-1; gi->qflag=0; #undef FREEFIELD for (p=gi->first; p; p=bkp) { bkp=p->next; free (p->member); free(p); } gi->first=gi->last=NULL; gi->nbMembers=0; } static int fix_nw_group_info (struct nw_group_info *gi ,struct nss_ncp_conf* conf){ /* fill NDS missing attributes with default values any group have a cn, and if gid was not found in NDS we will later consider that the corresponding Unix group is not activated unless a fallback value is defined in conf */ if (gi->cn) { if (gi->gid== (gid_t)-1) gi->gid=conf->fallbackGid; gi->passwd= gi->passwd ? gi->passwd: strdup("x"); // cannot read passwd from NDS if (!gi->passwd) { traceForce(conf->debug,LOG_ERR, "not enough memory when allocating password for group %s\n ",gi->cn); return 1; } if (gi->alias) { if (gi->cn) free(gi->cn); gi->cn=strdup(gi->alias); if (!gi->cn) { traceForce(conf->debug,LOG_ERR, "not enough memory when allocating alias for group %s\n ",gi->alias); return 1; } } }else { gi->gid = (gid_t)-1; // unable to read CN (NDS browse rights not set) , skip it ! } return 0; } /*********** return found info to caller in the format expected by NSS that is: filling the group structure as pointers to the passed buffer struct group { char *gr_name; // Group name. char *gr_passwd; // Password. __gid_t gr_gid; // Group ID. char **gr_mem; // Member list. }; ******************/ static enum nss_status nw_group_info_to_group(struct nw_group_info gi,struct group *grp, char * buffer, size_t buflen, int * errnop, struct nss_ncp_conf *conf){ if (gi.gid != (gid_t)-1) { grp->gr_gid=gi.gid; grp->gr_name=copy_to_buffer (&buffer,&buflen,gi.cn); if (!grp->gr_name) goto outnomem; grp->gr_passwd=copy_to_buffer (&buffer,&buflen,gi.passwd); if (!grp->gr_passwd) goto outnomem; {// copy members to buffer // names are stored backwards from end of buffer // and adress pointers forward from start of buffer // exit with NSS_STATUS_TRYAGAIN if buffer is too small // code inspired from nss_mysql by Guillaume Morin size_t required=0; char ** addPtr; char * end_of_buffer,*tmp,*nm; struct nw_group_member *p; for (p=gi.first;p;p=p->next) { required += strlen(p->member)+1+sizeof(char**); } if (required + sizeof(char**) >=buflen) { traceForce(conf->debug,LOG_ERR, "unable to copy members of group '%s' to buffer :need=%d have= %d\n",gi.cn,required,buflen); goto outnomem; } addPtr= (char**)buffer; grp->gr_mem=addPtr; end_of_buffer= buffer+buflen-1; p=gi.first; while (p) { end_of_buffer -=strlen(p->member)+1; tmp=end_of_buffer; //do not change end_of_buffer when copying the new member ! nm=copy_to_buffer(&tmp,&buflen,p->member); if (!nm) { traceForce(conf->debug,LOG_ERR, "not enough memory when copying nw_group_info to group for %s\n ",gi.cn); goto outnomem; } *addPtr=nm; //printf("%s=%s\n",p->member,*addPtr); addPtr++; p=p->next; } *addPtr=NULL; // end of table of pointers } *errnop=0; return NSS_STATUS_SUCCESS; outnomem: *errnop=ERANGE; return NSS_STATUS_TRYAGAIN; } else { trace(conf->debug,LOG_NOTICE, "group %s has no Unix GID in NDS\n ", gi.cn); *errnop=ENOENT; return NSS_STATUS_NOTFOUND; } } static void print_nw_group_info (struct nw_group_info gi){ struct nw_group_member* p; printf("%s:x:%d:%d members:",gi.cn,gi.gid,gi.nbMembers); for (p=gi.first;p;p=p->next) { printf("%s",p->member); if (p->next) printf(","); } printf("\n"); } static void print_group (struct group grp){ char ** mmb; int num; printf("%s:%s:%d:",grp.gr_name,grp.gr_passwd,grp.gr_gid); for (mmb=grp.gr_mem,num=0;*mmb; mmb++,num++) { if (num) printf(","); printf ("%s",*mmb); } printf("\n"); } /****************************************** internal structure for storing NDS user's shadow infos ***/ struct nw_shadow_info { char * cn; char * passwd; long int lstchg; long int sp_min; long int sp_max; long int sp_warn; long int sp_inact; long int sp_expire; unsigned long sp_flag; uid_t uid; int qflag; }; static void init_nw_shadow_info (struct nw_shadow_info *si,int qflag){ si->cn=NULL; si->passwd=NULL; si->lstchg=0; si->sp_min=0; si->sp_max=0; si->sp_warn=0; si->sp_inact=0; si->sp_expire=0; si->sp_flag=-1; si->uid=(uid_t)-1; si->qflag= qflag; } static void free_nw_shadow_info (struct nw_shadow_info *si){ #define FREEFIELD(x) do if (si->x) {free(si->x) ; si->x=NULL;} while (0); FREEFIELD(cn); FREEFIELD(passwd); si->lstchg=0; si->sp_min=0; si->sp_max=0; si->sp_warn=0; si->sp_inact=0; si->sp_expire=0; si->sp_flag=-1; si->uid=(uid_t)-1; si->qflag=0; #undef FREEFIELD } static int fix_nw_shadow_info (struct nw_shadow_info *si ,struct nss_ncp_conf *conf){ /* fill NDS missing attributes with default values any user have a cn, and if uid was not found in NDS we will later consider that the corresponding Unix account is not activated unless a fallback value is defined in conf */ if (si->cn) { if (si->uid== (uid_t)-1) si->uid=conf->fallbackUid; si->lstchg= si->lstchg ? si->lstchg : time(NULL)/24/3600; si->sp_min= si->sp_min ? si->sp_min: 0; si->sp_max= si->sp_max ? si->sp_max: 99999; si->sp_warn=si->sp_warn ? si->sp_warn: 7; si->passwd= si->passwd ? si->passwd: strdup("!!"); // cannot read passwd from NDS if (!si->passwd) { traceForce(conf->debug,LOG_ERR, "not enough memory when allocating password for shadow user %s\n ",si->cn); return 1; } }else { si->uid = (uid_t)-1; // unable to read CN (NDS browse rights not set) , skip it ! } return 0; } /*** return found info to caller in the format expected by NSS that is: filling the shadow structure as pointers to the passed buffer char *sp_namp; // Login nae. char *sp_pwdp; // Encrypted password. long int sp_lstchg; // Date of last change. long int sp_min; // Minimum number of days between changes. long int sp_max; // Maximum number of days between changes. long int sp_warn; // Number of days to warn user to change the password. long int sp_inact; // Number of days the account may be inactive. long int sp_expire; // Number of days since 1970-01-01 until account expires. unsigned long int sp_flag; // Reserved. *****/ static enum nss_status nw_shadow_info_to_shadow(struct nw_shadow_info si,struct spwd *spw, char * buffer, size_t buflen, int * errnop, struct nss_ncp_conf *conf){ if (si.uid != (uid_t)-1) { spw->sp_namp=copy_to_buffer (&buffer,&buflen,si.cn); if (!spw->sp_namp) goto outnomem; spw->sp_pwdp=copy_to_buffer (&buffer,&buflen,si.passwd); if (!spw->sp_pwdp) goto outnomem; spw->sp_lstchg=si.lstchg; spw->sp_min=si.sp_min; spw->sp_max=si.sp_max; spw->sp_warn=si.sp_warn; spw->sp_inact=si.sp_inact; spw->sp_expire=si.sp_expire; spw->sp_flag=si.sp_flag; *errnop=0; return NSS_STATUS_SUCCESS; outnomem: traceForce(conf->debug,LOG_ERR, "not enough memory when copying nw_shadow_info to shadow for %s\n ",si.cn); *errnop=ERANGE; return NSS_STATUS_TRYAGAIN; } else { trace(conf->debug,LOG_NOTICE, "user %s has no Unix UID in NDS\n ", si.cn); *errnop=ENOENT; return NSS_STATUS_NOTFOUND; } } static void print_nw_shadow_info (struct nw_shadow_info si){ printf("%s[%d]:%s:%ld:%ld:%ld:%ld:%ld:%ld:%ld\n",si.cn,si.uid,si.passwd,si.lstchg,si.sp_min,si.sp_max,si.sp_warn,si.sp_inact,si.sp_expire,si.sp_flag); } static void print_shadow (struct spwd spw){ printf("%s:%s:%ld:%ld:%ld:%ld:%ld:%ld:%ld\n",spw.sp_namp,spw.sp_pwdp,spw.sp_lstchg,spw.sp_min,spw.sp_max,spw.sp_warn,spw.sp_inact,spw.sp_expire,spw.sp_flag); } /****************************************** internal structure for storing NDS user's groups infos ***/ struct nw_user_group_info { char * cn; uid_t uid; gid_t* groups; size_t used; size_t alloc; int qflag; }; static void init_nw_user_group_info (struct nw_user_group_info *ui,int qflag){ ui->cn=NULL; ui->uid=(uid_t)-1; ui->groups=NULL; ui->used=0; ui->alloc=0; ui->qflag= qflag; } static void free_nw_user_group_info (struct nw_user_group_info *ui){ #define FREEFIELD(x) do if (ui->x) {free(ui->x) ; ui->x=NULL;} while (0); FREEFIELD(cn); FREEFIELD(groups); ui->used=0; ui->alloc=0; ui->uid=(uid_t)-1; ui->qflag=0; #undef FREEFIELD } static int fix_nw_user_group_info (struct nw_user_group_info *ui ,struct nss_ncp_conf * conf){ /* fill NDS missing attributes with default values any user have a cn, and if uid was not found in NDS we consider that the corresponding Unix account is not activated unless a fallback value is defined in conf */ if (ui->cn) { if (ui->uid== (uid_t)-1) ui->uid=conf->fallbackUid; }else { ui->uid = (uid_t)-1; // unable to read CN (NDS browse rights not set) , skip it ! } return 0; } // return found info to caller in the format expected by NSS that is: // filling up groups array // code similar to nss_ldap static enum nss_status nw_user_group_info_to_groups (struct nw_user_group_info ui,gid_t group, long int *start, long int *size, gid_t * groups,long int limit,int *errnop,struct nss_ncp_conf * conf) { if (ui.uid != (uid_t)-1) { size_t i; for (i=0; i < ui.used; i++) { gid_t gid=ui.groups[i]; if (gid != group) { // group number to skip if (*start == *size) { if (limit <=0) { // no more space, realloc if permitted (limit <=0) gid_t* ngroups=realloc(groups, 2* *size * sizeof(*groups)); if (!ngroups) { goto outnomem; } groups=ngroups; *size *=2; }else // no reallocation permitted, leave returning found groups so far break; } groups[*start]=gid; *start +=1; if (*start ==limit) { break; // stop storing gids and return found groups so far } } } *errnop=0; return NSS_STATUS_SUCCESS; outnomem: traceForce(conf->debug,LOG_ERR, "initgroups: not enough memory when reallocating group array for %s \n ",ui.cn); *errnop=ERANGE; return NSS_STATUS_TRYAGAIN; } else { trace(conf->debug,LOG_NOTICE, "user %s has no Unix UID in NDS\n ", ui.cn); *errnop=ENOENT; return NSS_STATUS_NOTFOUND; } } static void print_nw_user_group_info (struct nw_user_group_info ui){ size_t i; printf("%s:%d:%zd:%zd:",ui.cn,ui.uid,ui.used,ui.alloc); for (i=0;i attrname; ptr++) { dserr = NWDSPutAttrName(ctx, attrlist, ptr->attrname); if (dserr) { traceForce(debugMode,LOG_WARNING, "NWDSPutAttrName(%s) failed with %s\n", ptr->attrname, strnwerror(dserr)); goto bailoutbuf1; } } dserr = NWDSAllocBuf(DEFAULT_MESSAGE_LEN, &info); if (dserr) { traceForce(debugMode,LOG_WARNING, "NWDSAllocBuf() failed with %s\n", strnwerror(dserr)); goto bailoutbuf1; } iterHandle = NO_MORE_ITERATIONS; do { NWObjectCount attrs; dserr = NWDSRead(ctx, objname, DS_ATTRIBUTE_VALUES, 0, attrlist, &iterHandle, info); if (dserr) { if (dserr == ERR_NO_SUCH_ATTRIBUTE) dserr = 0;// attribute is missing . OK else traceForce(debugMode,LOG_WARNING, "NWDSRead() failed for %s with %s\n", objname,strnwerror(dserr)); goto bailoutbuf2; } dserr = NWDSGetAttrCount(ctx, info, &attrs); if (dserr) { traceForce(debugMode,LOG_WARNING, "NWDSGetAttrCount() failed with %s\n", strnwerror(dserr)); goto bailoutcloit; } while (attrs--) { NWDSChar attrname[MAX_SCHEMA_NAME_BYTES]; enum SYNTAX synt; NWObjectCount vals; dserr = NWDSGetAttrName(ctx, info, attrname, &vals, &synt); if (dserr) { traceForce(debugMode,LOG_WARNING, "NWDSGetAttrName() failed with %s\n", strnwerror(dserr)); goto bailoutcloit; } while (vals--) { size_t sz; void *val; dserr = NWDSComputeAttrValSize(ctx, info, synt, &sz); if (dserr) { traceForce(debugMode,LOG_WARNING, "NWDSComputeAttrValSize() failed with %s\n", strnwerror(dserr)); goto bailoutcloit; } val = malloc(sz); if (!val) { traceForce(debugMode,LOG_WARNING, "malloc() failed with %s\n", strnwerror(ENOMEM)); goto bailoutcloit; } dserr = NWDSGetAttrVal(ctx, info, synt, val); if (dserr) { free(val); traceForce(debugMode,LOG_WARNING, "NWDSGetAttrVal() failed with %s\n", strnwerror(dserr)); goto bailoutcloit; } for (ptr = atlist; ptr->attrname; ptr++) { if (!strcasecmp(ptr->attrname, attrname)) break; } if (ptr->getval) { if (ptr->synt != synt) { traceForce(debugMode,LOG_WARNING, "Incompatible tree schema, %s has syntax %d instead of %d\n", attrname, synt, ptr->synt); } else { // ajout PP dserr= !!! en cas de pb mémoire dserr = ptr->getval(ctx, val, arg); } } free(val); if (dserr) { goto bailoutcloit; } } } } while (iterHandle != NO_MORE_ITERATIONS); bailoutcloit:; if (iterHandle != NO_MORE_ITERATIONS) { NWDSCCODE dserr2 = NWDSCloseIteration(ctx, DSV_READ, iterHandle); if (dserr2) { traceForce(debugMode,LOG_WARNING, "NWDSCloseIteration() failed with %s\n", strnwerror(dserr2)); } } bailoutbuf2:; NWDSFreeBuf(info); bailoutbuf1:; NWDSFreeBuf(attrlist); bailout:; return dserr; } /*****************************************************GET USER INFO FROM NDS *************/ /************************************ helper functions to extract NDS properties ********/ // called as callbacks by nds_read_attrs static NWDSCCODE nds_user_cn(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; if (!ui->cn) { char *v = strdup((const char *) val); if (!v) { traceForce(ui->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } ui->cn = v; trace(ui->qflag,LOG_NOTICE, "got a Unix cn %s from %s\n ", ui->cn, ATTR_CN); } return 0; } static NWDSCCODE nds_user_unixuid(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; if (ui->uid == (uid_t) -1) { ui->uid = *(const Integer_T *) val; // talk a bit (real NDS8 attribute or dummy ?) trace(ui->qflag,LOG_NOTICE, "got a Unix ID %d from %s\n ", ui->uid, ATTR_UID); } return 0; } static NWDSCCODE nds_user_unixpgid(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; if (ui->gid == (gid_t) -1) { ui->gid = *(const Integer_T *) val; // talk a bit (real NDS8 attribute or dummy ?) trace(ui->qflag,LOG_NOTICE, "got a Unix PGID %d from %s\n ", ui->gid, ATTR_PGID); } return 0; } // this is the same founction as above ??? // does Netware has two synonyms for the same property (UNIX:GID" // and UNIX:Primary GroupID??? static NWDSCCODE nds_user_unixgid(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; if (ui->gid == (gid_t) -1) { ui->gid = *(const Integer_T *) val; // talk a bit (real NDS8 attribute or dummy ?) trace(ui->qflag,LOG_NOTICE, "got a Unix GID %d from %s\n ", ui->gid, ATTR_GID); } return 0; } static NWDSCCODE nds_user_unixhome(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; if (!ui->dir) { char *v = strdup((const char *) val); if (!v) { traceForce(ui->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } ui->dir = v; // talk a bit (real NDS8 attribute or dummy ?) trace(ui->qflag,LOG_NOTICE, "got a Unix Home %s from %s\n ", ui->dir, ATTR_HOME); } return 0; } static NWDSCCODE nds_user_unixshell(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; if (!ui->shell) { char *v = strdup((const char *) val); if (!v) { traceForce(ui->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } ui->shell = v; // talk a bit (real NDS8 attribute or dummy ?) trace(ui->qflag,LOG_NOTICE, "got a Unix shell %s from %s\n ", ui->shell, ATTR_SHELL); } return 0; } static NWDSCCODE nds_update_gecos(struct nw_user_info *ui, const char *str) { char *v; size_t sadd = strlen(str) + 1; if (ui->gecos) { // already got the name size_t sold = strlen(ui->gecos); trace(ui->qflag,LOG_NOTICE, "extending gecos %d %d\n",sadd,sold); v = realloc(ui->gecos, sold + 1 + sadd); if (!v) { traceForce(ui->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } v[sold] = ','; memcpy(v + sold+1, str, sadd); // bizarre sold a disparu dans pam_ncp ???? } else { trace(ui->qflag,LOG_NOTICE, "creating gecos %d \n",sadd); v = malloc(sadd); if (!v) { traceForce(ui->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } memcpy(v, str, sadd); } ui->gecos = v; return 0; } // PP we append the Comment after the full name, separated by a comma static NWDSCCODE nds_user_unixcomment(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; // talk a bit (real NDS8 attribute or dummy ?) trace(ui->qflag,LOG_NOTICE, "got a Unix Comment %s from %s\n ", (const char *) val, ATTR_COM); return nds_update_gecos(ui, (const char *) val); } // PP can be any naming attribute returning a SYN_CI_STRING see define before nds_user_info() // PP we add the name before any comment that can be there static NWDSCCODE nds_user_gecos(NWDSContextHandle ctx, const void *val, void *arg) { struct nw_user_info *ui = (struct nw_user_info *) arg; NWDSCCODE err; trace(ui->qflag,LOG_NOTICE, "before full name gecos is %s\n ", ui->gecos ? : "(null)"); err = nds_update_gecos(ui, (const char *) val); if (err) return err; trace(ui->qflag,LOG_NOTICE, "after full name gecos is %s\n ", ui->gecos); return 0; } // PP: id no NDS8 is present, collect the user's basic Unix informations from the location // strings with the format X:nnnnnnnn , X = [U,G,H,S,P,O,C,Z] upper of lower case // Of course, even if NDS8 IS present, we still look at these, just in case the migration // is not complete and to look for the user's ZENFLAG static NWDSCCODE nds_user_location(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; const char *pt = (const char *) val; char *v; int n; int err; trace(ui->qflag,LOG_NOTICE, "start of NW location got %s\n ", pt); if (strlen(pt) > 2 && pt[1] == ':') { const char *cur_pt = pt + 2; switch (*pt) { case 'u': //user ID leading spaces not significant case 'U': if (ui->uid == (uid_t) -1) { // do not overwrite a DS 8 answer switch (getnumber(&n, &cur_pt)) { case 0: ui->uid = n; break; default: traceForce(ui->qflag,LOG_ERR, "Invalid user ID %s\n", pt); } } break; case 'g': // primary group number GID leading spaces not significant case 'G': if (ui->gid == (gid_t) -1) { // do not overwrite a DS 8 answer switch (getnumber(&n, &cur_pt)) { case 0: ui->gid = n; break; default: traceForce(ui->qflag,LOG_ERR, "Invalid primary user GID %s\n", pt); } } break; case 'h': // home Unix all spaces significant (must have none ?) case 'H': if (!ui->dir) { // do not overwrite a DS 8 answer v = strdup(cur_pt); if (!v) { traceForce(ui->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } ui->dir = v; } break; case 's': //shell Unix all spaces significant (must have none ?) case 'S': if (!ui->shell) { // do not overwrite a DS 8 answer v = strdup(cur_pt); if (!v) { traceForce(ui->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } ui->shell = v; } break; case 'c': // comment all spaces significant. Will be appended to the gecos naming case 'C': // attribute with a comma and set by calling chfn -f xxxx -o xxxx // if comma are present in the string chfn will fails trace(ui->qflag,LOG_NOTICE, "before comment gecos is %s\n ", ui->gecos); err = nds_update_gecos(ui, cur_pt); if (err) return err; trace(ui->qflag,LOG_NOTICE, "gecos %s\n ", ui->gecos); break; } } return 0; } static NWDSCCODE nds_user_location2(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_info *ui = (struct nw_user_info *) arg; const char *pt = (const char *) val; int n; trace(ui->qflag,LOG_NOTICE, "start of NW location got %s\n ", pt); if (strlen(pt) > 2 && pt[1] == ':') { const char *cur_pt = pt + 2; switch (*pt) { case 'u': //user ID leading spaces not significant case 'U': if (ui->uid == (uid_t) -1) { // do not overwrite a DS 8 answer switch (getnumber(&n, &cur_pt)) { case 0: ui->uid = n; break; default: traceForce(ui->qflag,LOG_ERR, "Invalid user ID %s\n", pt); } } } } return 0; } static NWDSCCODE nds_user_info(NWDSContextHandle ctx, const NWDSChar * objname, void *ui, int modeDebug){ static const struct attrop atlist[] = { {ATTR_CN, nds_user_cn, SYN_CN}, {ATTR_UID, nds_user_unixuid, SYN_UID}, {ATTR_PGID, nds_user_unixpgid, SYN_PGID}, {ATTR_GID, nds_user_unixgid, SYN_GID}, {ATTR_HOME, nds_user_unixhome, SYN_HOME}, {ATTR_SHELL, nds_user_unixshell, SYN_SHELL}, {ATTR_COM, nds_user_unixcomment, SYN_COM}, {ATTR_GECOS, nds_user_gecos, SYN_CI_STRING}, {NULL, NULL, SYN_UNKNOWN} }; static const struct attrop atlist2[] = { {ATTR_LOCATION, nds_user_location, SYN_LOCATION}, {NULL, NULL, SYN_UNKNOWN} }; NWDSCCODE err; // we must do TWO NDS queries since NDS does not return attributes in this order // studies of /var/log/secure showed that L attribute usually come out before the NDS8 ones ! err = nds_read_attrs(ctx, objname, ui, atlist,modeDebug); if (err) return err; return nds_read_attrs(ctx, objname, ui, atlist2,modeDebug); } /***************** GET limited user's information (used when searching for group members) */ /* we search only UID either stored in NDS8 attributes ( Real or Dummy) or in Location */ static NWDSCCODE nds_user_info2(NWDSContextHandle ctx, const NWDSChar * objname, void *ui,int modeDebug){ static const struct attrop atlist[] = { {ATTR_CN, nds_user_cn, SYN_CN}, {ATTR_UID, nds_user_unixuid, SYN_UID}, {NULL, NULL, SYN_UNKNOWN} }; static const struct attrop atlist2[] = { {ATTR_LOCATION, nds_user_location2, SYN_LOCATION}, {NULL, NULL, SYN_UNKNOWN} }; NWDSCCODE err; // we must do TWO NDS queries since NDS does not return attributes in this order // studies of /var/log/secure showed that L attribute usually come out before the NDS8 ones ! err = nds_read_attrs(ctx, objname, ui, atlist,modeDebug); if (err) return err; return nds_read_attrs(ctx, objname, ui, atlist2,modeDebug); } /*****************************************************GET GROUP INFO FROM NDS *************/ /** gather Unix groups informations */ static NWDSCCODE nds_group_cn(NWDSContextHandle ctx, const void *val, void *arg) { struct nw_group_info *gi = (struct nw_group_info *) arg; if (!gi->cn) { char *v = strdup((const char *) val); if (!v) { traceForce(gi->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } gi->cn = v; trace(gi->qflag,LOG_NOTICE, "got a Unix cn %s from %s\n ", gi->cn, ATTR_CN); } return 0; } static NWDSCCODE nds_group_members(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_group_info *gi = (struct nw_group_info *) arg; NWDSCCODE err=0; struct nw_user_info ui; struct nw_group_member* newMember; init_nw_user_info(&ui,gi->qflag); //check whether member has some Unix properties err=nds_user_info2(ctx, (const char *)val, &ui,gi->qflag); if (!err && ui.uid !=(uid_t)-1 && ui.cn) { char *v = strdup(ui.cn); if (!v ) { traceForce(gi->qflag,LOG_WARNING, "Not enough memory for adding member %s tp group %s\n",ui.cn,gi->cn); err=ENOMEM; } else { newMember=malloc(sizeof(*newMember)); if (newMember) { newMember->member=v; newMember->next=NULL; if (!gi->first) gi->first=newMember; else gi->last->next=newMember; gi->last=newMember; gi->nbMembers++; trace(gi->qflag,LOG_NOTICE, "got a Unix members %s from %s\n ", ui.cn,gi->cn); } else { free(v); traceForce(gi->qflag,LOG_WARNING, "Not enough memory for adding member %s tp group %s\n",ui.cn,gi->cn); err=ENOMEM; } } } free_nw_user_info(&ui); return err; } static NWDSCCODE nds_group_unixgid(NWDSContextHandle ctx, const void* val, void* arg) { struct nw_group_info* gi = (struct nw_group_info*)arg; if (gi->gid == (gid_t)-1) { gi->gid = *(const Integer_T*)val; // talk a bit (real NDS8 attribute or dummy ?) trace(gi->qflag,LOG_NOTICE, "got a Unix GID %d from %s\n ", gi->gid, ATTR_GID); } return 0; } // PP: id no NDS8 is present, collect the group Unix ID from one of the location // string with the format G:nnn // can also used to specify a name of unix group different of the NDS'one // eg. everyone --> users or staff --> root static NWDSCCODE nds_group_location(NWDSContextHandle ctx, const void* val, void* arg) { struct nw_group_info* gi = (struct nw_group_info*)arg; const char *pt= (const char*) val; int n; if (strlen(pt)>2 && pt[1]==':') { const char* cur_pt=pt+2; switch (*pt) { case 'g': case 'G':if (gi->gid == (gid_t)-1) { switch (getnumber(&n,&cur_pt)) { case 0: gi->gid=n; break; default:traceForce(gi->qflag,LOG_ERR, "Invalid group GID %s for %s\n",pt,gi->cn); } } break; case 'n': case 'N': // unix equivalent name if (!gi->alias) { char* v = strdup(cur_pt); if (!v) { traceForce(gi->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } gi->alias = v; trace(gi->qflag,LOG_NOTICE, "group:got a Unix alias %s from %s\n ", gi->alias, gi->cn); } break; } } return 0; } static NWDSCCODE nds_group_info(NWDSContextHandle ctx, const NWDSChar* objname, void * gi, int modeDebug) { static const struct attrop atlist[] = { { ATTR_CN, nds_group_cn, SYN_CN }, { ATTR_GID, nds_group_unixgid, SYN_GID }, { ATTR_MEMBERS, nds_group_members, SYN_MEMBERS}, { NULL, NULL, SYN_UNKNOWN }}; static const struct attrop atlist2[] = { {ATTR_LOCATION, nds_group_location, SYN_LOCATION}, {NULL, NULL, SYN_UNKNOWN}}; NWDSCCODE err; // we must do TWO NDS queries since NDS does not return attributes in this order // studies of /var/log/secure showed that L attribute usually come out before the NDS8 ones ! err = nds_read_attrs(ctx, objname, gi, atlist,modeDebug); if (err) return err; return nds_read_attrs(ctx, objname, gi, atlist2,modeDebug); } /***************** GET limited group's information (used when searching for groups to which a user belongs) */ static NWDSCCODE nds_group_location2(NWDSContextHandle ctx, const void* val, void* arg) { struct nw_group_info* gi = (struct nw_group_info*)arg; const char *pt= (const char*) val; int n; if (strlen(pt)>2 && pt[1]==':') { const char* cur_pt=pt+2; switch (*pt) { case 'g': case 'G':if (gi->gid == (gid_t)-1) { switch (getnumber(&n,&cur_pt)) { case 0: gi->gid=n; break; default:traceForce(gi->qflag,LOG_ERR, "Invalid group GID %s for %s\n",pt,gi->cn); } } break; } } return 0; } /* we search only GID either stored in NDS8 attributes ( Real or Dummy) or in Location */ static NWDSCCODE nds_group_info2(NWDSContextHandle ctx, const NWDSChar* objname, void * gi, int modeDebug) { static const struct attrop atlist[] = { { ATTR_GID, nds_group_unixgid, SYN_GID }, { NULL, NULL, SYN_UNKNOWN }}; static const struct attrop atlist2[] = { {ATTR_LOCATION, nds_group_location2, SYN_LOCATION}, {NULL, NULL, SYN_UNKNOWN}}; NWDSCCODE err; // we must do TWO NDS queries since NDS does not return attributes in this order // studies of /var/log/secure showed that L attribute usually come out before the NDS8 ones ! err = nds_read_attrs(ctx, objname, gi, atlist,modeDebug); if (err) return err; return nds_read_attrs(ctx, objname, gi, atlist2,modeDebug); } /*****************************************************GET SHADOW INFO FROM NDS *************/ static NWDSCCODE nds_shadow_cn(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_shadow_info *si = (struct nw_shadow_info *) arg; if (!si->cn) { char *v = strdup((const char *) val); if (!v) { traceForce(si->qflag & QF_DEBUG,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } si->cn = v; trace(si->qflag & QF_DEBUG,LOG_NOTICE, "shadow:got a Unix cn %s from %s\n ", si->cn, ATTR_CN); } return 0; } static NWDSCCODE nds_shadow_unixuid(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_shadow_info *si = (struct nw_shadow_info *) arg; if (si->uid == (uid_t) -1) { si->uid = *(const Integer_T *) val; // talk a bit (real NDS8 attribute or dummy ?) trace(si->qflag & QF_DEBUG,LOG_NOTICE, "shadow: got a Unix ID %d from %s\n ", si->uid, ATTR_UID); } return 0; } // PP: id no NDS8 is present, collect the user's basic Unix informations from the location // strings with the format X:nnnnnnnn , X = [U,G,H,S,P,O,C,Z] upper of lower case // Of course, even if NDS8 IS present, we still look at these, just in case the migration // is not complete and to look for the user's ZENFLAG static NWDSCCODE nds_shadow_location(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_shadow_info *si = (struct nw_shadow_info *) arg; const char *pt = (const char *) val; int n; trace(si->qflag & QF_DEBUG,LOG_NOTICE, "shadow: start of NW location got %s\n ", pt); if (strlen(pt) > 2 && pt[1] == ':') { const char *cur_pt = pt + 2; switch (*pt) { case 'u': //user ID leading spaces not significant case 'U': if (si->uid == (uid_t) -1) { // do not overwrite a DS 8 answer switch (getnumber(&n, &cur_pt)) { case 0: si->uid = n; break; default: traceForce(si->qflag & QF_DEBUG,LOG_ERR, "shadow:Invalid user ID %s\n", pt); } } break; } } return 0; } static NWDSCCODE nds_shadow_pwd_expire(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_shadow_info *si = (struct nw_shadow_info *) arg; si->sp_expire = *(const Time_T *) val/3600/24; return 0; } static NWDSCCODE nds_shadow_int_pwd_expire(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_shadow_info *si = (struct nw_shadow_info *) arg; si->sp_min = *(const Integer_T *) val/3600/24; si->sp_max = *(const Integer_T *) val/3600/24; return 0; } static NWDSCCODE nds_shadow_acct_expire(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_shadow_info *si = (struct nw_shadow_info *) arg; si->sp_inact = *(const Time_T *) val/3600/24; return 0; } static NWDSCCODE nds_shadow_grace_pwd_expire (NWDSContextHandle ctx, const void *val, void *arg) { // in NDS it is the number of grace login, in Unix the number of DAYS struct nw_shadow_info *si = (struct nw_shadow_info *) arg; si->sp_warn = *(const Integer_T *) val; return 0; } /*****************************************************GET USER SHADOW INFOS FROM NDS *************/ static NWDSCCODE nds_shadow_info(NWDSContextHandle ctx, const NWDSChar * objname, void *si,int modeDebug){ static const struct attrop atlist[] = { {ATTR_CN, nds_shadow_cn, SYN_CN}, {ATTR_UID, nds_shadow_unixuid, SYN_UID}, {ATTR_DATE_PWD_EXPIRE,nds_shadow_pwd_expire,SYN_TIME}, {ATTR_DATE_ACCT_EXPIRE,nds_shadow_acct_expire,SYN_TIME}, {ATTR_INT_PWD_EXPIRE,nds_shadow_int_pwd_expire,SYN_INTERVAL}, {ATTR_GRACE_LIMIT,nds_shadow_grace_pwd_expire,SYN_INTEGER}, {NULL, NULL, SYN_UNKNOWN} }; static const struct attrop atlist2[] = { {ATTR_LOCATION, nds_shadow_location, SYN_LOCATION}, {NULL, NULL, SYN_UNKNOWN} }; NWDSCCODE err; // we must do TWO NDS queries since NDS does not return attributes in this order // studies of /var/log/secure showed that L attribute usually come out before the NDS8 ones ! err = nds_read_attrs(ctx, objname, si, atlist,modeDebug); if (err) return err; return nds_read_attrs(ctx, objname, si, atlist2,modeDebug); } /***************get all group id of groups userName belongs to *********************/ static NWDSCCODE nds_user_cn2(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_group_info *ui = (struct nw_user_group_info *) arg; if (!ui->cn) { char *v = strdup((const char *) val); if (!v) { traceForce(ui->qflag,LOG_WARNING, "Not enough memory for strdup()\n"); return ENOMEM; } ui->cn = v; trace(ui->qflag & QF_DEBUG,LOG_NOTICE, "got a Unix cn %s from %s\n ", ui->cn, ATTR_CN); } return 0; } static NWDSCCODE nds_user_unixuid2(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_group_info *ui = (struct nw_user_group_info *) arg; if (ui->uid == (uid_t) -1) { ui->uid = *(const Integer_T *) val; // talk a bit (real NDS8 attribute or dummy ?) trace(ui->qflag & QF_DEBUG,LOG_NOTICE, "got a Unix ID %d from %s\n ", ui->uid, ATTR_UID); } return 0; } static NWDSCCODE nds_user_location3(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_group_info *ui = (struct nw_user_group_info *) arg; const char *pt = (const char *) val; int n; trace(ui->qflag & QF_DEBUG,LOG_NOTICE, "start of NW location got %s\n ", pt); if (strlen(pt) > 2 && pt[1] == ':') { const char *cur_pt = pt + 2; switch (*pt) { case 'u': //user ID leading spaces not significant case 'U': if (ui->uid == (uid_t) -1) { // do not overwrite a DS 8 answer switch (getnumber(&n, &cur_pt)) { case 0: ui->uid = n; break; default: traceForce(ui->qflag & QF_DEBUG,LOG_ERR, "Invalid user ID %s\n", pt); } } break; } } return 0; } static NWDSCCODE nds_get_one_user_group(NWDSContextHandle ctx, const void *val, void *arg){ struct nw_user_group_info *ui = (struct nw_user_group_info *) arg; struct nw_group_info gi; NWDSCCODE ccode; init_nw_group_info(&gi,ui->qflag); //check whether group has some Unix properties trace(ui->qflag & QF_DEBUG,LOG_NOTICE, "found group %s for user %s\n ", (const char*)val, ui->cn); ccode=nds_group_info2(ctx,(const char *)val,&gi,ui->qflag); // found a real GID , no fallback here else all groups would have the same number !!!! if (!ccode && gi.gid != (gid_t) -1) { trace(ui->qflag & QF_DEBUG,LOG_NOTICE, "found group GID %d for user %s\n ", gi.gid, ui->cn); if (ui->used >=ui->alloc) { gid_t* np; size_t ns; if (ui->groups) { ns=ui->alloc +8; np=(gid_t*)realloc (ui->groups,ns*sizeof(gid_t)); }else { ns=8; np=(gid_t*)malloc (ns*sizeof(gid_t)); } if (!np) { traceForce(ui->qflag & QF_DEBUG,LOG_WARNING, "Not enough memory for allocating GID table\n"); return ENOMEM; } ui->groups=np; ui->alloc=ns; } ui->groups[ui->used++]=gi.gid; } free_nw_group_info(&gi); return ccode; } static NWDSCCODE nds_get_user_groups(NWDSContextHandle ctx, const NWDSChar * objname, void *ui, int modeDebug){ // get all groups of userName static const struct attrop atlist[] = { {ATTR_CN, nds_user_cn2, SYN_CN}, {ATTR_UID, nds_user_unixuid2, SYN_UID}, {ATTR_GRP_MBS, nds_get_one_user_group, SYN_GRP_MBS}, {NULL, NULL, SYN_UNKNOWN} }; static const struct attrop atlist2[] = { {ATTR_LOCATION, nds_user_location3, SYN_LOCATION}, {NULL, NULL, SYN_UNKNOWN} }; NWDSCCODE err; // we must do TWO NDS queries since NDS does not return attributes in this order // studies of /var/log/secure showed that L attribute usually come out before the NDS8 ones ! err = nds_read_attrs(ctx, objname, ui, atlist,modeDebug); if (err) return err; return nds_read_attrs(ctx, objname, ui, atlist2,modeDebug); } /*******************************************************************************************************************/ static NWDSCCODE CreateContextAndConn ( NWDSContextHandle *context,NWCONN_HANDLE *conn, struct nss_ncp_conf* conf) { NWDSCCODE ccode; nuint32 contextFlags; trace(conf->debug, LOG_NOTICE,"Entering create context and conn"); ccode = NWDSCreateContextHandle(context); if(ccode) { traceForce(conf->debug,LOG_WARNING,"Error creating context.\n"); goto Exit1; } trace(conf->debug, LOG_NOTICE,"CreateContextHandle OK"); //ccode=NWDSSetContext(*context, DCK_NAME_CONTEXT, conf->startCtx); ccode=NWDSSetContext(*context, DCK_NAME_CONTEXT, "[Root]"); if(ccode){ traceForce(conf->debug,LOG_WARNING,"Error NWDSSetContext(): %d\n",ccode); goto Exit2; } trace(conf->debug, LOG_NOTICE,"SetContext OK"); ccode= NWDSGetContext(*context, DCK_FLAGS, &contextFlags); if( ccode){ traceForce(conf->debug,LOG_WARNING,"NWDSGetContext (DCK_FLAGS) failed, returns %d\n", ccode); goto Exit2; } trace(conf->debug, LOG_NOTICE,"GetContext OK"); contextFlags|= DCV_TYPELESS_NAMES; ccode= NWDSSetContext( *context, DCK_FLAGS, &contextFlags); if( ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSSetContext (DCK_FLAGS DCV_TYPELESS_NAMES) failed, returns %d\n", ccode); goto Exit2; } trace(conf->debug, LOG_NOTICE,"SetContext OK"); trace(conf->debug, LOG_NOTICE,"calling NWCCOpenConnByName ..."); if (conf->server[0] == '/') //using a permanent connection ccode = ncp_open_mount(conf->server, conn); else { if (!conf->useTree) ccode = NWCCOpenConnByName(NULL, conf->server, NWCC_NAME_FORMAT_BIND, 0, 0, conn); else //ccode=NWCXAttachToTreeByName( conn, conf->server); ccode = NWCCOpenConnByName(NULL, conf->server, NWCC_NAME_FORMAT_NDS_TREE, 0, 0, conn); if (ccode) { traceForce(conf->debug,LOG_WARNING,"Error: NWCCOpenConnByName failed %s\n",strnwerror(ccode)); goto Exit2; } } trace(conf->debug, LOG_NOTICE,"NWCCOpenConnByName OK"); ccode= NWDSAddConnection(*context, *conn); if (ccode) { traceForce(conf->debug,LOG_WARNING,"Error: NWCCOpenConnByName failed %s\n",strnwerror(ccode)); goto Exit2; } trace(conf->debug, LOG_NOTICE,"AddConnection OK"); if (conf->debug & QF_DEBUG) { char aux[512]; aux[0] = 0; if (!conf->useTree) { NWCCGetConnInfo(*conn, NWCC_INFO_TREE_NAME, sizeof (aux), aux); trace(conf->debug, LOG_DEBUG, "sucessful connection to tree %s by server %s \n",aux,conf->server); }else { NWCCGetConnInfo(*conn, NWCC_INFO_SERVER_NAME, sizeof (aux), aux); trace(conf->debug,LOG_DEBUG, "successful connection by NDS server %s on tree %s\n", aux,conf->server); } } Exit2: if (ccode) { trace(conf->debug, LOG_NOTICE,"Closing context and conn due to errors"); if(conn) NWCCCloseConn (*conn); if (context) NWDSFreeContext(*context); *context=NULL; *conn=NULL; } Exit1: trace(conf->debug, LOG_NOTICE,"leaving CreateContextAndConn"); return ccode; } /**** PP TreeScaning routines ***********************/ struct TreeNode { struct TreeNode* left; struct TreeNode* right; struct TreeNode* next; struct TreeNode** pprev; size_t cnt; char name [MAX_DN_CHARS+1]; }; struct ObjectList { struct TreeNode* first; struct TreeNode* lin; struct TreeNode* curr; int dups; size_t uniqueObjects; size_t remainObjects; size_t totalObjects; NWDSContextHandle context; NWCONN_HANDLE conn; struct nss_ncp_conf * conf; }; static struct ObjectList* __allocTree(int dups,struct nss_ncp_conf * conf) { struct ObjectList* t; t = (struct ObjectList*)malloc(sizeof(*t)); if (t) { t->first = NULL; t->lin = NULL; t->curr = NULL; t->dups = dups; t->uniqueObjects = 0; t->conf=conf; } return t; } static void __freeNode(struct TreeNode* n) { while (n) { struct TreeNode* tmp; __freeNode(n->left); tmp = n; n = n->right; free(tmp); } } static void __freeTree(struct ObjectList* t) { if (t) { struct TreeNode* n = t->first; if (t->conf) free_nss_ncp_conf(t->conf); free(t); __freeNode(n); } } static NWDSCCODE __allocNode(struct TreeNode** pn, const char* objectName) { struct TreeNode* n; size_t len=strlen(objectName); if (len > MAX_DN_CHARS) return NWE_BUFFER_OVERFLOW; n = (struct TreeNode*)malloc(sizeof(*n)); if (!n) return ERR_NOT_ENOUGH_MEMORY; n->left = n->right = NULL; n->cnt = 1; memcpy(n->name, objectName, len + 1); *pn = n; return 0; } static NWDSCCODE __insertNode(struct ObjectList* t, const char* objectName) { struct TreeNode** p; struct TreeNode* n; NWDSCCODE err; p = &t->first; while ((n = *p) != NULL) { int cmp = strcasecmp(objectName, n->name); if (cmp < 0) { p = &n->left; if (!*p) { err = __allocNode(p, objectName); if (!err) { struct TreeNode* q = *p; q->next = n; q->pprev = n->pprev; n->pprev = &q->next; *(q->pprev) = q; t->uniqueObjects++; t->totalObjects++; } return err; } } else if (cmp > 0) { p = &n->right; if (!*p) { err = __allocNode(p, objectName); if (!err) { struct TreeNode* q = *p; q->next = n->next; if (q->next) q->next->pprev = &q->next; n->next = q; q->pprev = &n->next; t->uniqueObjects++; t->totalObjects++; } return err; } } else { if (t->dups) { n->cnt++; t->totalObjects++; return 0; } else { return EINVAL; // no dups allowed and one found } } } err = __allocNode(p, objectName); if (!err) { struct TreeNode* q = *p; t->lin = q; q->next = NULL; q->pprev = &t->lin; t->uniqueObjects++; t->totalObjects++; } return err; } static void print_nodes(struct TreeNode* n, int crlf) { if (n) { print_nodes(n->left,crlf); if (crlf) printf ("%s [%zd]\n",n->name,n->cnt); else printf ("%s [%zd]",n->name,n->cnt); print_nodes(n->right,crlf); } } static void print_tree(struct ObjectList* t, int crlf) { printf("total:%zd unique:%zd\n",t->totalObjects,t->uniqueObjects); print_nodes (t->first,crlf); } // callback for getallentxx() static NWDSCCODE nds_insert_info(NWDSContextHandle ctx, const NWDSChar * objectName, void *tree, int modeDebug){ return __insertNode((struct ObjectList*)tree, objectName); } /* eot*/ /*** collecting in a tree every member of a group, if his UID defined in NDS */ static NWDSCCODE nds_get_group_members(NWDSContextHandle ctx, const void *val, void *arg){ struct ObjectList *tree = (struct ObjectList *) arg; NWDSCCODE ccode=0; struct nw_user_info ui; init_nw_user_info(&ui,0); //check whether member has some Unix properties ccode=nds_user_info2(ctx, (const char *)val, &ui,tree->conf->debug); // found a UID real (no fallback otherwise all users would have the same UID) if (!ccode && ui.uid !=(uid_t)-1) { ccode=__insertNode(tree,(const char *)val); } free_nw_user_info(&ui); return ccode; } static NWDSCCODE getgroupmembers(NWDSContextHandle *context,NWCONN_HANDLE *conn, const char* groupName,struct ObjectList * tree, struct nss_ncp_conf* conf){ // get all members of a group NWDSCCODE ccode; static const struct attrop atlist[] = { { ATTR_MEMBERS, nds_get_group_members, SYN_MEMBERS}, { NULL, NULL, SYN_UNKNOWN }}; trace(conf->debug, LOG_NOTICE,"entering getgroupmembers for group %s",groupName); ccode=CreateContextAndConn ( context,conn,conf); if (ccode) return ccode; ccode=nds_read_attrs(*context, groupName, tree, atlist,conf->debug); trace(conf->debug, LOG_NOTICE,"leaving getgroupmembers for group %s err=%s",groupName,strnwerror(ccode)); return ccode; } // static lists. Allocated by nss_ncp_setxxent_r, used buy nss_ncp_getxxent_r and released by nss_ncp_endxxent_r // TODO :mutex and threads needed as in nss_ldap or nss_mysql ??? static struct ObjectList* ndsUsers=NULL; static struct ObjectList* ndsGroups=NULL; static struct ObjectList* ndsShadows=NULL; // description of a NDS class to be searched by getentbyxx struct class_info { const char * className; const char * nds8Attribute; // name of ID attribute in NDS8 const char * LID1; // markers in L attribute for ID (U: or G:) const char * LID2; // markers in L attribute for ID (u: or g:), may be in lower case const char * LAlias1; // markers in L attribute for alias (N:) const char * LAlias2; // markers in L attribute for alias (n:) may be in lower case }; // we care only about user's and group classes static struct class_info USER_CLASS = {"User", ATTR_UID,"U:","u:","N:","n:"}; static struct class_info GROUP_CLASS= {"Group",ATTR_GID,"G:","g:","N:","n:"}; /*protoptype of callback functions used in getentbyxx*/ struct infoop { NWDSCCODE (*getinfo) (NWDSContextHandle ctx , const NWDSChar * objectName, void * info, int debug); }; /* currently match nds_user_info(context,objectName,ui,modeDebug); nds_user_info2(context,objectName,ui,modeDebug); nds_group_info(context,objectName,gi,modeDebug); nds_group_info2(context,objectName,gi,modeDebug); nds_shadow_info(context,objectName,si,modeDebug); nds_insert_info(context,objectName,tree,modeDebug): */ // generic NDS search routine static NWDSCCODE getentbyxx( NWDSContextHandle *retContext, // context to return (NULL= close when leaving) NWCONN_HANDLE *retConn, // connexion to return (NULL= close when leaving) struct class_info classType, // class to search (User of Group) void *info, // structure to fill (can be nw_user_info,nw_shadow_info,nw_group_info or a ObjectList) const struct infoop callBack, // routine to call for all found NDS objects const char *unixName, // Unix objet name to find in NDS (if NULL search by ID) uid_t id, // Unix ID to find in NDS , if -1 search by unixName) int allEntries, // Ignore unixName and id and return all entries belonging to classType.className struct nss_ncp_conf *conf) { // configuration record (debug...) NWDSContextHandle context; NWCONN_HANDLE conn; NWDSCCODE ccode; nuint32 iterationHandle = NO_MORE_ITERATIONS; // to be set as such at Exit4 nuint32 countObjectsSearched; nuint32 objCntr; nuint32 objCount; nuint32 attrCount; char objectName[MAX_DN_CHARS+1]; // buffers pBuf_T searchFilter=NULL; // search filter pBuf_T retBuf=NULL; // result buffer for NWDSSearch pBuf_T attrNames=NULL; // specify which attribute values to return Filter_Cursor_T* cur=NULL; // search expression tree temporary buffer Object_Info_T objectInfo; //few checks if (!allEntries) { if (unixName && id !=(uid_t)-1) return EINVAL; if (!unixName && id ==(uid_t)-1) return EINVAL; } trace(conf->debug, LOG_NOTICE,"entering getentbyxx"); ccode=CreateContextAndConn ( &context,&conn,conf); if (ccode) return ccode; trace(conf->debug, LOG_NOTICE,"context and conn OK"); /* In order to search, we need: A Filter Cursor (to build the search expression) A Filter Buffer (to store the expression; used by NWDSSearch) A Buffer to store which attributes we need information on A Result Buffer (to store the search results) */ ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN,&searchFilter); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAllocBuf returned: %d\n", ccode); goto Exit3; } trace(conf->debug, LOG_NOTICE,"NWDSAllocBuf searchFilter OK"); // Initialize the searchFilter buffer ccode = NWDSInitBuf(context,DSV_SEARCH_FILTER,searchFilter); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSInitBuf returned: %d\n", ccode); goto Exit4; } trace(conf->debug, LOG_NOTICE, "NWDSInitBuf searchFilter OK"); // Allocate a filter cursor to put the search expression ccode = NWDSAllocFilter(&cur); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAllocFilter returned: %d\n", ccode); goto Exit4; } trace(conf->debug, LOG_NOTICE,"NWDSAllocFilter cur OK"); // Build the expression tree in cur, then place into searchFilter // Object Class = User ccode = NWDSAddFilterToken(cur,FTOK_ANAME,"Object Class",SYN_CLASS_NAME); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken OBJECTCLASS returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_EQ,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_EQ returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_AVAL,classType.className,SYN_CLASS_NAME); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_AVAL User returned: %d\n", ccode); goto Exit4; } trace(conf->debug, LOG_NOTICE,"AddFilter for classname OK"); if (!allEntries) { ccode = NWDSAddFilterToken(cur,FTOK_AND,NULL,0); if (ccode ) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_AND returned: %d\n", ccode); goto Exit4; } if (unixName) { /* search for the CN or an alias in location strings */ /* CN=unixName or L=N:unixNname or l=n:unixName */ char buf1[255]; char buf2[255]; if (strlen(unixName)+3 >sizeof(buf1)) { //buffer overflow !!! ccode=EINVAL; traceForce(conf->debug,LOG_WARNING,"unixName %s is too long !!!!\n", unixName); goto Exit4; } sprintf (buf1,"%s%s",classType.LAlias1,unixName); sprintf (buf2,"%s%s",classType.LAlias2,unixName); ccode = NWDSAddFilterToken(cur,FTOK_LPAREN,NULL,0); if (ccode ) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_RPAREN returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_ANAME,"CN",0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_ANAME CN returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_EQ,NULL,0); if (ccode ) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_EQ returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_AVAL,unixName,SYN_CI_STRING); if (ccode ) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_AVAL %s returned: %d\n", unixName,ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_OR,NULL,0); if (ccode ) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_OR returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_ANAME,"L",0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_ANAME L returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_EQ,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_EQ returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_AVAL,buf1,SYN_CI_STRING); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_VAL %s returned: %d\n", buf1,ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_OR,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_OR returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_ANAME,"L",0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_ANAME returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_EQ,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_END returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_AVAL,buf2,SYN_CI_STRING); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_AVAL %s returned: %d\n", buf2,ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_RPAREN,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_RPAREN returned: %d\n", ccode); goto Exit4; } } else { if (id !=(uid_t)-1) { /* search by UID.or GID AND (L="U:xxxxx" OR L="u:xxxx" OR UNIX:UID = xxxx) for users AND (L="G:xxxxx" OR L="g:xxxx" OR UNIX:GID = xxxx) for groups */ char buf1[80]; char buf2[80]; sprintf (buf1,"%s%d",classType.LID1,id); sprintf (buf2,"%s%d",classType.LID2,id); ccode = NWDSAddFilterToken(cur,FTOK_LPAREN,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_LPAREN returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_ANAME,"L",0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_ANAME L returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_EQ,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_EQ returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_AVAL,buf1,SYN_CI_STRING); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_VAL %s returned: %d\n", buf1,ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_OR,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_OR returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_ANAME,"L",0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_ANAME returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_EQ,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_END returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_AVAL,buf2,SYN_CI_STRING); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_AVAL %s returned: %d\n", buf2,ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_OR,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_OR returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_ANAME,classType.nds8Attribute,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_ANAME returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_EQ,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_END returned: %d\n", ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_AVAL,&buf1[2],SYN_INTEGER); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_AVAL %s returned: %d\n", buf2,ccode); goto Exit4; } ccode = NWDSAddFilterToken(cur,FTOK_RPAREN,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_RPAREN returned: %d\n", ccode); goto Exit4; } } } } ccode = NWDSAddFilterToken(cur,FTOK_END,NULL,0); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAddFilterToken FTOK_END returned: %d\n", ccode); goto Exit4; } trace(conf->debug, LOG_NOTICE,"AddFilterToken FTOK_END OK"); // now place the cursor into the searchFilter buffer // NWDSPutFilter frees the expression tree filter (cur) // so if it succeeds, set cur to NULL so it won't be freed below ccode = NWDSPutFilter(context,searchFilter,cur,NULL); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSPutFilter returned: %d\n", ccode); goto Exit4; } else cur=NULL; trace(conf->debug, LOG_NOTICE,"PutFilter OK"); // allocate and initialize the attrNames buffer (not used , needed ???) ccode = NWDSAllocBuf(DEFAULT_MESSAGE_LEN,&attrNames); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAllocBuf returned: %d\n", ccode); goto Exit4; } trace(conf->debug, LOG_NOTICE,"AllocBuf attrNamesOK"); ccode = NWDSInitBuf(context,DSV_SEARCH,attrNames); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSInitBuf returned: %d\n", ccode); goto Exit4; } trace(conf->debug, LOG_NOTICE,"InitBuf attrNames OK"); // Allocate a result buffer ccode = NWDSAllocBuf(65500,&retBuf); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSAllocBuf returned: %d\n", ccode); goto Exit4; } trace(conf->debug, LOG_NOTICE,"AllocBuf retBuf OK, start searching..."); // PP 11-01-2003 // with NCP_DEBUG=cleanup, if we exit here, usecount of the connection is still 1 (OK) // so usecount is augmented below //ccode=-1; //goto Exit4; iterationHandle = NO_MORE_ITERATIONS; // while NWDSSearch still can get some objects...( I expect 0 or 1) do { ccode = NWDSSearch(context, //"[Root]", conf->startCtx, DS_SEARCH_SUBTREE, FALSE, // don't dereference aliases searchFilter, FALSE, // we want ONLY attributes names FALSE, // only want information in attrNames attrNames, &iterationHandle, 0, // reserved &countObjectsSearched, retBuf); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSSearch returned: %s\n", strnwerror(ccode)); goto Exit4; } // PP 11-01-2003 // with NCP_DEBUG=cleanup, if we exit here, usecount of the connection is now 2 (bad) // so it is NWDSSearch that augmented usecount !!!! //ccode=-1; //goto Exit4; trace(conf->debug, LOG_NOTICE,"NWDSearch OK"); // count the object returned in the buffer ccode = NWDSGetObjectCount(context,retBuf,&objCount); if (ccode) { traceForce(conf->debug,LOG_WARNING,"NWDSGetObjectCount returned: %d\n", ccode); goto Exit4; } if (objCount <= 0) { ccode=-1; goto Exit4; } if ( !allEntries && (objCount >1)) { if (unixName) traceForce(conf->debug,LOG_WARNING,"more than one NDS entry match the name=%s criteria. ",unixName); else traceForce(conf->debug,LOG_WARNING,"more than one NDS entry match the id=%d criteria. ",id); } trace(conf->debug, LOG_NOTICE,"GetObjectCount OK"); // for the number of objects returned... // for nss it should be only one !!! for (objCntr=0;objCntrdebug,LOG_WARNING,"NWDSGetObjectName returned: %d\n", ccode); goto Exit4; } trace(conf->debug, LOG_NOTICE,"GetObjectName OK"); if (callBack.getinfo) { ccode= callBack.getinfo(context,objectName,info,conf->debug); if (ccode) goto Exit4; } } trace(conf->debug, LOG_NOTICE,"callback return OK"); } while (iterationHandle != NO_MORE_ITERATIONS); trace(conf->debug, LOG_NOTICE,"End of iteration attrNamesOK"); Exit4: if (iterationHandle != NO_MORE_ITERATIONS){ NWDSCCODE ccode2; if ((ccode2=NWDSCloseIteration(context,iterationHandle,DSV_SEARCH)) != 0) { traceForce(conf->debug,LOG_WARNING,"NWDSCloseIteration returned: %d\n", ccode2); } } if (retBuf) NWDSFreeBuf(retBuf); if (cur) NWDSFreeFilter(cur, NULL); if (searchFilter) NWDSFreeBuf(searchFilter); if (attrNames) NWDSFreeBuf(attrNames); trace(conf->debug, LOG_NOTICE,"All buffers cleaned OK"); Exit3: if (ccode || !retConn){ NWDSCCODE ccode2; trace(conf->debug,LOG_NOTICE,"Closing connection %lx",conn); ccode2=NWCCCloseConn (conn); if (ccode2) traceForce(conf->debug,LOG_WARNING,"NWCCloseConnection returned: %d %s\n", ccode,strnwerror(ccode2)); }else if (retConn) *retConn=conn; if (ccode || !retContext) { NWDSCCODE ccode2=NWDSFreeContext(context); trace(conf->debug, LOG_NOTICE,"Freeing context"); if (ccode2) traceForce(conf->debug,LOG_WARNING,"NWDSFreeContext returned: %d %s\n", ccode,strnwerror(ccode2)); }else if (retContext) *retContext=context; trace(conf->debug, LOG_NOTICE,"Leaving ..."); return ccode; } static NWDSCCODE getpwdentbyxx(struct nw_user_info *ui, const char* userName, uid_t userId, struct nss_ncp_conf *conf){ static const struct infoop getInfo={nds_user_info}; return getentbyxx( NULL, // context to return (NULL= close when leaving) NULL, // connexion to return (NULL= close when leaving) USER_CLASS, // class to search (User of Group) ui, // structure to fill (can be nw_user_info,nw_shadow_info,nw_group_info) getInfo, // routine to call for all found NDS objects userName, // Unix objet name to find in NDS (if NULL search by ID) userId, // Unix ID to find in NDS , if -1 search by unixName) FALSE, // Ignore objectName and id and return all entries belonging to className conf); // debug flag } static NWDSCCODE getgrpentbyxx(struct nw_group_info *gi, const char* groupName, gid_t groupId, struct nss_ncp_conf *conf){ static const struct infoop getInfo={nds_group_info}; return getentbyxx( NULL, // context to return (NULL= close when leaving) NULL, // connexion to return (NULL= close when leaving) GROUP_CLASS, // class to search (User of Group) gi, // structure to fill (can be nw_user_info,nw_shadow_info,nw_group_info) getInfo, // routine to call for all found NDS objects groupName, // Unix objet name to find in NDS (if NULL search by ID) groupId, // Unix ID to find in NDS , if -1 search by unixName) FALSE, // Ignore objectName and id and return all entries belonging to className conf); // debug flag } static NWDSCCODE getspentbyxx(struct nw_shadow_info *si, const char* userName, struct nss_ncp_conf *conf){ // in shadow we search only by userName static const struct infoop getInfo={nds_shadow_info}; return getentbyxx( NULL, // context to return (NULL= close when leaving) NULL, // connexion to return (NULL= close when leaving) USER_CLASS, // class to search (User of Group) si, // structure to fill (can be nw_user_info,nw_shadow_info,nw_group_info) getInfo, // routine to call for all found NDS objects userName, // Unix objet name to find in NDS (if NULL search by ID) (uid_t)-1, // Unix ID to find in NDS , if -1 search by unixName) FALSE, // Ignore objectName and id and return all entries belonging to className conf); // debug flag } static NWDSCCODE getusergroupsbyxx(struct nw_user_group_info *ui, const char* userName, uid_t userId, struct nss_ncp_conf *conf){ static const struct infoop getInfo={nds_get_user_groups}; return getentbyxx( NULL, // context to return (NULL= close when leaving) NULL, // connexion to return (NULL= close when leaving) USER_CLASS, // class to search (User of Group) ui, // structure to fill (can be nw_user_info,nw_shadow_info,nw_group_info) getInfo, // routine to call for all found NDS objects userName, // Unix objet name to find in NDS (if NULL search by ID) userId, // Unix ID to find in NDS , if -1 search by unixName) FALSE, // Ignore objectName and id and return all entries belonging to className conf); // debug flag } static NWDSCCODE getallents(NWDSContextHandle *context,NWCONN_HANDLE *conn, const struct class_info classType, struct ObjectList * tree, struct nss_ncp_conf *conf){ // called by all setxxent() if not control group is present in conf // get all entries by className // and add them to a sorted tree in memory // return context and conncetion for later use by static const struct infoop getInfo={nds_insert_info}; return getentbyxx( context, // context to return (NULL= close when leaving) conn, // connexion to return (NULL= close when leaving) classType, // class to search (User of Group) tree, // structure to fill (can be nw_user_info,nw_shadow_info,nw_group_info) getInfo, // routine to call for all found NDS objects NULL, // Unix objet name to find in NDS (if NULL search by ID) (uid_t)-1, // Unix ID to find in NDS , if -1 search by unixName) TRUE, // Ignore objectName and id and return all entries belonging to className conf); // debug flag } /*******************************************************************************/ /******************************* NSS API ***************************************/ enum nss_status _nss_ncp_initgroups (const char *userName, gid_t group, long int *start, long int *size, gid_t * groups, long int limit,int *errnop) { struct nw_user_group_info inf; NWDSCCODE err; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doGroup) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_initgroups entering"); } init_nw_user_group_info(&inf,conf->debug); err= getusergroupsbyxx(&inf, userName, (uid_t)-1,conf); switch (err) { case 0: if (conf->debug &QF_VERBOSE) print_nw_user_group_info(inf); if (!fix_nw_user_group_info(&inf,conf)) err=nw_user_group_info_to_groups(inf,group,start,size,groups,limit,errnop,conf); else err=NSS_STATUS_UNAVAIL; break; case -1: // NOT FOUND IN NDS err=NSS_STATUS_NOTFOUND; *errnop=ENOENT; break; default: // NDS error err=NSS_STATUS_UNAVAIL; *errnop=ENOENT; break; } free_nw_user_group_info(&inf); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_initgroups leaving"); closelog(); } return err; } enum nss_status _nss_ncp_getpwnam_r (const char* name, struct passwd *pwd, char * buffer, size_t buflen, int * errnop) { struct nw_user_info inf; NWDSCCODE err; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doPassword) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_getpwnam entering"); } init_nw_user_info(&inf,conf->debug); err= getpwdentbyxx(&inf, name, (uid_t)-1,conf); switch (err) { case 0: if (conf->debug &QF_VERBOSE) print_nw_user_info(inf); if (!fix_nw_user_info(&inf,conf)) err=nw_user_info_to_passwd(inf,pwd,buffer,buflen,errnop,conf); else err=NSS_STATUS_UNAVAIL; break; case -1: // NOT FOUND IN NDS err=NSS_STATUS_NOTFOUND; *errnop=ENOENT; break; default: // NDS error err=NSS_STATUS_UNAVAIL; *errnop=ENOENT; break; } free_nw_user_info(&inf); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_getpwnam leaving"); closelog(); } return err; } enum nss_status _nss_ncp_getpwuid_r (uid_t uid, struct passwd *pwd, char * buffer, size_t buflen, int * errnop) { struct nw_user_info inf; NWDSCCODE err; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doPassword) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_getpwuid entering"); } init_nw_user_info(&inf,conf->debug); err= getpwdentbyxx(&inf, NULL, uid,conf); switch (err) { case 0: if (conf->debug &QF_VERBOSE) print_nw_user_info(inf); if (!fix_nw_user_info(&inf,conf)) err=nw_user_info_to_passwd(inf,pwd,buffer,buflen,errnop,conf); else err=NSS_STATUS_UNAVAIL; break; case -1: // NOT FOUND IN NDS err=NSS_STATUS_NOTFOUND; *errnop=ENOENT; break; default: // NDS error err=NSS_STATUS_UNAVAIL; *errnop=ENOENT; break; } free_nw_user_info(&inf); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_getpwuid leaving"); closelog(); } return err; } enum nss_status _nss_ncp_getgrnam_r (const char* name, struct group *grp, char * buffer, size_t buflen, int * errnop) { struct nw_group_info inf; NWDSCCODE err; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doGroup) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_getgrnam entering"); } init_nw_group_info(&inf,conf->debug); err= getgrpentbyxx(&inf, name, (gid_t)-1,conf); switch (err) { case 0: if (conf->debug &QF_VERBOSE) print_nw_group_info(inf); if (!fix_nw_group_info(&inf,conf)) err=nw_group_info_to_group(inf,grp,buffer,buflen,errnop,conf); else err=NSS_STATUS_UNAVAIL; break; case -1: // NOT FOUND IN NDS err=NSS_STATUS_NOTFOUND; *errnop=ENOENT; break; default: // NDS error err=NSS_STATUS_UNAVAIL; *errnop=ENOENT; break; } free_nw_group_info(&inf); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_getgrnam leaving"); closelog(); } return err; } enum nss_status _nss_ncp_getspnam_r (const char * name, struct spwd *spw, char *buffer, size_t buflen,int * errnop) { struct nw_shadow_info inf; NWDSCCODE err; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doShadow) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_getspnam entering"); } init_nw_shadow_info(&inf,conf->debug); err= getspentbyxx(&inf, name,conf); switch (err) { case 0: if (conf->debug &QF_VERBOSE) print_nw_shadow_info(inf); if (!fix_nw_shadow_info(&inf,conf)) err=nw_shadow_info_to_shadow(inf,spw,buffer,buflen,errnop,conf); else err=NSS_STATUS_UNAVAIL; break; case -1: // NOT FOUND IN NDS err=NSS_STATUS_NOTFOUND; *errnop=ENOENT; break; default: // NDS error err=NSS_STATUS_UNAVAIL; *errnop=ENOENT; break; } free_nw_shadow_info(&inf); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_getspnam leaving"); closelog(); } return err; } enum nss_status _nss_ncp_getgrgid_r (gid_t gid, struct group *grp, char * buffer, size_t buflen, int * errnop) { struct nw_group_info inf; NWDSCCODE err; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doGroup) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_getgrgid entering"); } init_nw_group_info(&inf,conf->debug); err= getgrpentbyxx(&inf, NULL, gid,conf); switch (err) { case 0: if (conf->debug &QF_VERBOSE) print_nw_group_info(inf); if (!fix_nw_group_info(&inf,conf)) err=nw_group_info_to_group(inf,grp,buffer,buflen,errnop,conf); else err=NSS_STATUS_UNAVAIL; break; case -1: // NOT FOUND IN NDS err=NSS_STATUS_NOTFOUND; *errnop=ENOENT; break; default: // NDS error err=NSS_STATUS_UNAVAIL; *errnop=ENOENT; break; } free_nw_group_info(&inf); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_getgrid leaving"); closelog(); } return err; } enum nss_status _nss_ncp_setpwent(void) { NWDSContextHandle context; NWCONN_HANDLE conn; NWDSCCODE ccode; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doPassword) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_setpwent entering"); } ndsUsers= __allocTree(FALSE,conf); if (ndsUsers) { if (conf->ctrlGroup && conf->ctrlGroup[0]) // caution if empty string from conf or default ccode=getgroupmembers(&context,&conn,conf->ctrlGroup,ndsUsers,conf); else ccode=getallents(&context,&conn,USER_CLASS,ndsUsers,conf); if (!ccode) { ndsUsers->context=context; ndsUsers->conn=conn; ndsUsers->curr = ndsUsers->lin; ndsUsers->remainObjects = ndsUsers->uniqueObjects; ccode= NSS_STATUS_SUCCESS; } else { __freeTree(ndsUsers); ndsUsers=NULL; ccode= NSS_STATUS_UNAVAIL; } }else ccode= NSS_STATUS_UNAVAIL; if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_setpwent leaving"); closelog(); } return ccode; } enum nss_status _nss_ncp_setgrent(void) { NWDSContextHandle context; NWCONN_HANDLE conn; enum nss_status ccode; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doGroup) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_setgrent entering"); } ndsGroups= __allocTree(FALSE,conf); if (ndsGroups) { if (! getallents(&context,&conn,GROUP_CLASS,ndsGroups,conf)) { ndsGroups->context=context; ndsGroups->conn=conn; ndsGroups->curr = ndsGroups->lin; ndsGroups->remainObjects = ndsGroups->uniqueObjects; ccode=NSS_STATUS_SUCCESS; } else { __freeTree(ndsGroups); ndsGroups=NULL; ccode=NSS_STATUS_UNAVAIL; } }else ccode= NSS_STATUS_UNAVAIL; if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_setgrent leaving"); closelog(); } return ccode; } enum nss_status _nss_ncp_setspent (void) { NWDSContextHandle context; NWCONN_HANDLE conn; NWDSCCODE ccode; struct nss_ncp_conf* conf = parse_conf(); if (!conf || !conf->doShadow) return NSS_STATUS_UNAVAIL; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_setspent entering"); } ndsShadows= __allocTree(FALSE,conf); if (ndsShadows) { if (conf->ctrlGroup && conf->ctrlGroup[0]) // caution if empty string from conf or default ccode=getgroupmembers(&context,&conn,conf->ctrlGroup,ndsShadows,conf); else ccode=getallents(&context,&conn,USER_CLASS,ndsShadows,conf); if (!ccode) { ndsShadows->context=context; ndsShadows->conn=conn; ndsShadows->curr = ndsShadows->lin; ndsShadows->remainObjects = ndsShadows->uniqueObjects; ccode=NSS_STATUS_SUCCESS; } else { __freeTree(ndsShadows); ndsShadows=NULL; ccode=NSS_STATUS_UNAVAIL; } }else ccode= NSS_STATUS_UNAVAIL; if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_setspent leaving"); closelog(); } return ccode; } enum nss_status _nss_ncp_endpwent(void) { enum nss_status ccode; if (ndsUsers) { struct nss_ncp_conf *conf=ndsUsers->conf; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_endpwent entering"); } NWCCCloseConn (ndsUsers->conn); NWDSFreeContext(ndsUsers->context); __freeTree(ndsUsers); ndsUsers=NULL; trace (conf->debug, LOG_NOTICE,"nss_ncp_setpwent all cleaned up OK"); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_setpwent leaving"); closelog(); } ccode=NSS_STATUS_SUCCESS; }else { trace (QF_DEBUG, LOG_ERR,"nss_ncp_endpwent called without a previous nss_ncp_setpwent: nothing to cleanup"); ccode=NSS_STATUS_UNAVAIL; } return ccode; } enum nss_status _nss_ncp_endgrent(void) { enum nss_status ccode; if (ndsGroups) { struct nss_ncp_conf *conf=ndsGroups->conf; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_endgrent entering"); } NWCCCloseConn (ndsGroups->conn); NWDSFreeContext(ndsGroups->context); __freeTree(ndsGroups); ndsGroups=NULL; trace (conf->debug, LOG_NOTICE,"nss_ncp_endgrent all cleaned up OK"); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_endpwent leaving"); closelog(); } ccode=NSS_STATUS_SUCCESS; }else { trace (QF_DEBUG, LOG_ERR,"nss_ncp_endgrent called without a previous nss_ncp_setgrent : nothing to cleanup"); ccode=NSS_STATUS_UNAVAIL; } return ccode; } enum nss_status _nss_ncp_endspent (void) { enum nss_status ccode; if (ndsShadows) { struct nss_ncp_conf *conf=ndsShadows->conf; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_endspent entering"); } NWCCCloseConn (ndsShadows->conn); NWDSFreeContext(ndsShadows->context); __freeTree(ndsShadows); ndsShadows=NULL; trace (conf->debug, LOG_NOTICE,"nss_ncp_endspent all cleaned up up OK"); if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_endspent leaving"); closelog(); } ccode=NSS_STATUS_SUCCESS; }else { trace (QF_DEBUG, LOG_ERR,"nss_ncp_endspent called without a previous nss_ncp_setspent : nothing to cleanup"); ccode=NSS_STATUS_UNAVAIL; } return ccode; } enum nss_status _nss_ncp_getpwent_r(struct passwd *pwd, char * buffer, size_t buflen, int * errnop) { enum nss_status err; if (ndsUsers) { struct TreeNode* n; NWDSCCODE ccode; struct nw_user_info ui; struct nss_ncp_conf *conf=ndsUsers->conf; const char* ndsName; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_getpwent entering"); } next_user:; // avoid a recursive call n = ndsUsers->curr; if (!n) { //ready to scan again from the beginning (a good idea ??) ndsUsers->curr = ndsUsers->lin; ndsUsers->remainObjects = ndsUsers->uniqueObjects; err= NSS_STATUS_NOTFOUND; } else { ndsName=n->name; init_nw_user_info(&ui,conf->debug); ccode=nds_user_info(ndsUsers->context,n->name,&ui,conf->debug); if (ccode) { free_nw_user_info(&ui); err=NSS_STATUS_NOTFOUND; goto exit; } if (conf->debug &QF_VERBOSE) print_nw_user_info(ui); if (!fix_nw_user_info(&ui,conf)) err=nw_user_info_to_passwd(ui,pwd,buffer,buflen,&errno,conf); else err=NSS_STATUS_UNAVAIL; free_nw_user_info(&ui); switch (err) { case NSS_STATUS_TRYAGAIN: // buffer was too small // do not advance to next user // and let nss to try again with a bigger buffer; break; default: // move to next user if (!--n->cnt) { ndsUsers->curr = n->next; ndsUsers->remainObjects--; } if (err==NSS_STATUS_NOTFOUND) {// user has no unix property, skip trace (conf->debug, LOG_NOTICE,"user %s has not Unix properties in NDS,skipping",ndsName); goto next_user; }else err=NSS_STATUS_SUCCESS; } } exit: if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_getpwent leaving"); closelog(); } }else { err=NSS_STATUS_UNAVAIL; } return err; } enum nss_status _nss_ncp_getgrent_r(struct group *grp, char * buffer, size_t buflen, int * errnop) { enum nss_status err; if (ndsGroups) { struct TreeNode* n; NWDSCCODE ccode; const char* ndsName; struct nw_group_info gi; struct nss_ncp_conf *conf=ndsGroups->conf; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_getgrent entering"); } next_group:; // avoid a recursive call n = ndsGroups->curr; if (!n) { //ready to scan again from the beginning (a good idea ??) ndsGroups->curr = ndsGroups->lin; ndsGroups->remainObjects = ndsGroups->uniqueObjects; err= NSS_STATUS_NOTFOUND; }else { ndsName=n->name; init_nw_group_info(&gi,conf->debug); ccode=nds_group_info(ndsGroups->context,n->name,&gi,conf->debug); if (ccode) { free_nw_group_info(&gi); err= NSS_STATUS_NOTFOUND; goto exit; } if (conf->debug &QF_VERBOSE) print_nw_group_info(gi); if (!fix_nw_group_info(&gi,conf)) err=nw_group_info_to_group(gi,grp,buffer,buflen,&errno,conf); else err=NSS_STATUS_UNAVAIL; free_nw_group_info(&gi); switch (err) { case NSS_STATUS_TRYAGAIN: // buffer was too small // do not advance to next group // and let nss to try again with a bigger buffer break; default: // move to next group if (!--n->cnt) { ndsGroups->curr = n->next; ndsGroups->remainObjects--; } if (err==NSS_STATUS_NOTFOUND) {// group has no unix property, skip trace (conf->debug, LOG_NOTICE,"group %s has not Unix properties in NDS,skipping",ndsName); goto next_group; } else err=NSS_STATUS_SUCCESS; } } exit: if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_getgrent leaving"); closelog(); } }else { err= NSS_STATUS_UNAVAIL; } return err; } enum nss_status _nss_ncp_getspent_r (struct spwd *spw, char * buffer, size_t buflen,int * errnop) { enum nss_status err; if (ndsShadows) { struct TreeNode* n; NWDSCCODE ccode; const char* ndsName; struct nw_shadow_info si; struct nss_ncp_conf *conf=ndsShadows->conf; if (conf->debug) { openlog("ncp_nss", LOG_PID, LOG_AUTHPRIV); trace (conf->debug, LOG_NOTICE,"nss_ncp_getgrent entering"); } next_user:; // avoid a recursive call n = ndsShadows->curr; if (!n) { //ready to scan again from the beginning (a good idea ??) ndsShadows->curr = ndsShadows->lin; ndsShadows->remainObjects = ndsShadows->uniqueObjects; err= NSS_STATUS_NOTFOUND; }else { ndsName=n->name; init_nw_shadow_info(&si,conf->debug); ccode=nds_shadow_info(ndsShadows->context,n->name,&si,conf->debug); if (ccode) { free_nw_shadow_info(&si); err=NSS_STATUS_NOTFOUND; goto exit; } if (conf->debug &QF_VERBOSE) print_nw_shadow_info(si); if (!fix_nw_shadow_info(&si,conf)) err=nw_shadow_info_to_shadow(si,spw,buffer,buflen,&errno,conf); else err=NSS_STATUS_UNAVAIL; free_nw_shadow_info(&si); switch (err) { case NSS_STATUS_TRYAGAIN: // buffer was too small // do not advance to next user // and let nss to try again with a bigger buffer; break; default: // move to next user if (!--n->cnt) { ndsShadows->curr = n->next; ndsShadows->remainObjects--; } if (err==NSS_STATUS_NOTFOUND) {// user has no unix property, skip trace (conf->debug, LOG_NOTICE,"user %s has not Unix properties in NDS,skipping",ndsName); goto next_user; } else err=NSS_STATUS_SUCCESS; } } exit: if (conf->debug) { trace (conf->debug, LOG_NOTICE,"nss_ncp_getspent leaving"); closelog(); } }else { return NSS_STATUS_UNAVAIL; } return err; }