diff options
author | Arthur de Jong <arthur@arthurdejong.org> | 2010-12-29 22:50:31 +0000 |
---|---|---|
committer | Arthur de Jong <arthur@arthurdejong.org> | 2010-12-29 22:50:31 +0000 |
commit | e985efa83458e1cc9c2bcb12e3cc10b6526c3399 (patch) | |
tree | 8311cb525c9d452d62d88280e6cca854496f9c42 | |
parent | 4e9224817ee303404b804a1a51f2f9c9a49164e4 (diff) | |
parent | ed6bc27721075adf0215ad8b856fcdcf7b98b9b7 (diff) |
merge changes from trunk
git-svn-id: http://arthurdejong.org/svn/nss-pam-ldapd/nss-pam-ldapd-solaris@1349 ef36b2f9-881f-0410-afb5-c4e39611909c
35 files changed, 2011 insertions, 88 deletions
diff --git a/Makefile.am b/Makefile.am index bc162dc..e4b0f90 100644 --- a/Makefile.am +++ b/Makefile.am @@ -29,6 +29,9 @@ endif if ENABLE_NSLCD SUBDIRS += nslcd endif +if ENABLE_PYNSLCD + SUBDIRS += pynslcd +endif SUBDIRS += man tests DEBIAN_FILES = debian/changelog debian/compat debian/control \ @@ -64,7 +67,7 @@ DEBIAN_FILES = debian/changelog debian/compat debian/control \ EXTRA_DIST = nslcd.conf nslcd.h $(wildcard ChangeLog-20??) \ $(wildcard m4/*.m4) HACKING $(DEBIAN_FILES) -DISTCHECK_CONFIGURE_FLAGS = --enable-warnings +DISTCHECK_CONFIGURE_FLAGS = --enable-warnings --with-pam-seclib-dir="\$${libdir}/security" ACLOCAL_AMFLAGS = -I m4 diff --git a/common/tio.c b/common/tio.c index 0914a4a..468d5cb 100644 --- a/common/tio.c +++ b/common/tio.c @@ -295,7 +295,12 @@ int tio_read(TFILE *fp, void *buf, size_t count) /* read the input in the buffer */ rv=read(fp->fd,fp->readbuffer.buffer+fp->readbuffer.start,fp->readbuffer.size-fp->readbuffer.start); /* check for errors */ - if ((rv==0)||((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN))) + if (rv==0) + { + errno=ECONNRESET; + return -1; + } + else if ((rv<0)&&(errno!=EINTR)&&(errno!=EAGAIN)) return -1; /* something went wrong with the read */ /* skip the read part in the buffer */ fp->readbuffer.len=rv; diff --git a/configure.ac b/configure.ac index e47926b..6e94574 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,7 @@ AC_PROG_INSTALL AC_PROG_RANLIB AM_PROG_CC_C_O AC_USE_SYSTEM_EXTENSIONS +AC_PROG_LN_S # checks for tool to convert docbook to man AC_PATH_PROGS(DOCBOOK2X_MAN, docbook2x-man) @@ -132,14 +133,27 @@ AC_MSG_RESULT($enable_pam) AM_CONDITIONAL([ENABLE_PAM], [test "x$enable_pam" = "xyes"]) # check whether the nslcd daemon should be built -AC_MSG_CHECKING([whether to build the nslcd server]) +AC_MSG_CHECKING([whether to build the nslcd daemon]) AC_ARG_ENABLE(nslcd, AS_HELP_STRING([--disable-nslcd], - [build the nslcd server [[default=enabled]]]),, + [build the nslcd daemon [[default=enabled]]]),, [enable_nslcd="yes"]) AC_MSG_RESULT($enable_nslcd) AM_CONDITIONAL([ENABLE_NSLCD], [test "x$enable_nslcd" = "xyes"]) +# check whether the Python version of the nslcd daemon should be built +AC_MSG_CHECKING([whether to build the pynslcd daemon]) +AC_ARG_ENABLE(pynslcd, + AS_HELP_STRING([--enable-pynslcd], + [build the pynslcd daemon [[default=disabled]]]),, + [enable_pynslcd="no"]) +AC_MSG_RESULT($enable_pynslcd) +AM_CONDITIONAL([ENABLE_PYNSLCD], [test "x$enable_pynslcd" = "xyes"]) +if test "x$enable_pynslcd" = "xyes" +then + AC_MSG_WARN([the pynslcd daemon is experimental]) +fi + # check whether SASL support should be enabled AC_MSG_CHECKING([whether to enable SASL support]) AC_ARG_ENABLE(sasl, @@ -169,6 +183,7 @@ AC_MSG_RESULT($configfile_checking) if test "x$configfile_checking" = "xyes" then AC_DEFINE(ENABLE_CONFIGFILE_CHECKING,1,[Whether to check configfile options.]) + AC_SUBST(ENABLE_CONFIGFILE_CHECKING,1) fi # check the name of the configuration file @@ -705,10 +720,18 @@ then AC_SUBST(nslcd_LIBS) fi +# pynslcd daemon-specific tests +if test "x$enable_pynslcd" = "xyes" +then + # check Python interpreter + AM_PATH_PYTHON(2.5) +fi + AM_CONDITIONAL([NSS_FLAVOUR_GLIBC], [test "x${with_nss_flavour}" = xglibc]) AM_CONDITIONAL([NSS_FLAVOUR_SOLARIS], [test "x${with_nss_flavour}" = xsolaris]) # generate files AC_CONFIG_FILES([Makefile compat/Makefile common/Makefile nss/Makefile - pam/Makefile nslcd/Makefile man/Makefile tests/Makefile]) + pam/Makefile nslcd/Makefile pynslcd/Makefile pynslcd/config.py + man/Makefile tests/Makefile]) AC_OUTPUT diff --git a/man/nslcd.conf.5.xml b/man/nslcd.conf.5.xml index 72f0c21..62d249d 100644 --- a/man/nslcd.conf.5.xml +++ b/man/nslcd.conf.5.xml @@ -399,17 +399,25 @@ See the section on attribute mapping expressions below for more details. </para> <para> - Only some attributes for passwd and shadow entries may be mapped with - an expression (because other attributes may be used in search + Only some attributes for group, passwd and shadow entries may be mapped + with an expression (because other attributes may be used in search filters). + For group entries only the <literal>userPassword</literal> attribute + may be mapped with an expression. For passwd entries the following attributes may be mapped with an - expression: <literal>gidNumber</literal>, <literal>gecos</literal>, - <literal>homeDirectory</literal> and <literal>loginShell</literal>. + expression: <literal>userPassword</literal>, <literal>gidNumber</literal>, + <literal>gecos</literal>, <literal>homeDirectory</literal> and + <literal>loginShell</literal>. For shadow entries the following attributes may be mapped with an - expression: <literal>shadowLastChange</literal>, <literal>shadowMin</literal>, - <literal>shadowMax</literal>, <literal>shadowWarning</literal>, - <literal>shadowInactive</literal>, <literal>shadowExpire</literal> and - <literal>shadowFlag</literal>. + expression: <literal>userPassword</literal>, <literal>shadowLastChange</literal>, + <literal>shadowMin</literal>, <literal>shadowMax</literal>, + <literal>shadowWarning</literal>, <literal>shadowInactive</literal>, + <literal>shadowExpire</literal> and <literal>shadowFlag</literal>. + </para> + <para> + By default all <literal>userPassword</literal> attributes are mapped + to the unmatchable password ("*") to avoid accidentally leaking + password information. </para> </listitem> </varlistentry> @@ -537,6 +545,8 @@ <para> Specifies the directory containing X.509 certificates for peer authentication. + This parameter is ignored when using GnuTLS. + On Debian OpenLDAP is linked against GnuTLS. </para> </listitem> </varlistentry> @@ -555,6 +565,8 @@ <listitem> <para> Specifies the path to an entropy source. + This parameter is ignored when using GnuTLS. + On Debian OpenLDAP is linked against GnuTLS. </para> </listitem> </varlistentry> @@ -649,6 +661,17 @@ </varlistentry> <varlistentry> + <term><option>nss_min_uid</option> <replaceable>UID</replaceable></term> + <listitem> + <para> + This option ensures that <acronym>LDAP</acronym> users with a numeric + user id lower than the specified value are ignored. Also requests for + users with a lower user id are ignored. + </para> + </listitem> + </varlistentry> + + <varlistentry> <term><option>pam_authz_search</option> <replaceable>FILTER</replaceable></term> <listitem> diff --git a/nslcd/attmap.c b/nslcd/attmap.c index 92cc011..32b8041 100644 --- a/nslcd/attmap.c +++ b/nslcd/attmap.c @@ -213,10 +213,13 @@ const char *attmap_set_mapping(const char **var,const char *value) /* these attributes may contain an expression (note that this needs to match the functionality in the specific lookup module) */ - if ( (var!=&attmap_passwd_gidNumber) && + if ( (var!=&attmap_group_userPassword) && + (var!=&attmap_passwd_userPassword) && + (var!=&attmap_passwd_gidNumber) && (var!=&attmap_passwd_gecos) && (var!=&attmap_passwd_homeDirectory) && (var!=&attmap_passwd_loginShell) && + (var!=&attmap_shadow_userPassword) && (var!=&attmap_shadow_shadowLastChange) && (var!=&attmap_shadow_shadowMin) && (var!=&attmap_shadow_shadowMax) && diff --git a/nslcd/cfg.c b/nslcd/cfg.c index 364e726..c2a5480 100644 --- a/nslcd/cfg.c +++ b/nslcd/cfg.c @@ -120,6 +120,7 @@ static void cfg_defaults(struct ldap_config *cfg) cfg->ldc_pagesize=0; cfg->ldc_nss_initgroups_ignoreusers=NULL; cfg->ldc_pam_authz_search=NULL; + cfg->ldc_nss_min_uid=0; } /* simple strdup wrapper */ @@ -985,7 +986,8 @@ static void cfg_read(const char *filename,struct ldap_config *cfg) LDAP_SET_OPTION(NULL,LDAP_OPT_X_TLS_CACERTDIR,value); free(value); } - else if (strcasecmp(keyword,"tls_cacertfile")==0) + else if ( (strcasecmp(keyword,"tls_cacertfile")==0) || + (strcasecmp(keyword,"tls_cacert")==0) ) { get_strdup(filename,lnr,keyword,&line,&value); get_eol(filename,lnr,keyword,&line); @@ -1050,6 +1052,11 @@ static void cfg_read(const char *filename,struct ldap_config *cfg) check_argumentcount(filename,lnr,keyword,(line!=NULL)&&(*line!='\0')); cfg->ldc_pam_authz_search=xstrdup(line); } + else if (strcasecmp(keyword,"nss_min_uid")==0) + { + get_uid(filename,lnr,keyword,&line,&cfg->ldc_nss_min_uid); + get_eol(filename,lnr,keyword,&line); + } #ifdef ENABLE_CONFIGFILE_CHECKING /* fallthrough */ else diff --git a/nslcd/cfg.h b/nslcd/cfg.h index de43956..a44d5d2 100644 --- a/nslcd/cfg.h +++ b/nslcd/cfg.h @@ -137,6 +137,8 @@ struct ldap_config SET *ldc_nss_initgroups_ignoreusers; /* the search that should be performed to do autorisation checks */ char *ldc_pam_authz_search; + /* minimum uid for users retreived from LDAP */ + uid_t ldc_nss_min_uid; }; /* this is a pointer to the global configuration, it should be available diff --git a/nslcd/common.c b/nslcd/common.c index d88bb60..dc25bed 100644 --- a/nslcd/common.c +++ b/nslcd/common.c @@ -35,6 +35,7 @@ #include "nslcd.h" #include "common.h" #include "log.h" +#include "attmap.h" /* simple wrapper around snptintf() to return non-0 in case of any failure (but always keep string 0-terminated) */ @@ -51,25 +52,21 @@ int mysnprintf(char *buffer,size_t buflen,const char *format, ...) return ((res<0)||(((size_t)res)>=buflen)); } -const char *get_userpassword(MYLDAP_ENTRY *entry,const char *attr) +const char *get_userpassword(MYLDAP_ENTRY *entry,const char *attr,char *buffer,size_t buflen) { - const char **values; - int i; - /* get the entries */ - values=myldap_get_values(entry,attr); - if ((values==NULL)||(values[0]==NULL)) + const char *tmpvalue; + /* get the value */ + tmpvalue=attmap_get_value(entry,attr,buffer,buflen); + if (tmpvalue==NULL) return NULL; /* go over the entries and return the remainder of the value if it starts with {crypt} or crypt$ */ - for (i=0;values[i]!=NULL;i++) - { - if (strncasecmp(values[i],"{crypt}",7)==0) - return values[i]+7; - if (strncasecmp(values[i],"crypt$",6)==0) - return values[i]+6; - } + if (strncasecmp(tmpvalue,"{crypt}",7)==0) + return tmpvalue+7; + if (strncasecmp(tmpvalue,"crypt$",6)==0) + return tmpvalue+6; /* just return the first value completely */ - return values[0]; + return tmpvalue; /* TODO: support more password formats e.g. SMD5 (which is $1$ but in a different format) (any code for this is more than welcome) */ @@ -100,13 +97,20 @@ int isvalidname(const char *name) if (i>=LOGIN_NAME_MAX) return 0; #endif /* LOGIN_NAME_MAX */ - if ( ! ( ( (i!=0) && (name[i]=='-') ) || - ( (i!=0) && (name[i]=='\\') && name[i+1]!='\0' ) || - (name[i]>='@' && name[i] <= 'Z') || - (name[i]>='a' && name[i] <= 'z') || - (name[i]>='0' && name[i] <= '9') || - name[i]=='.' || name[i]=='_' || name[i]=='$' || name[i]==' ') ) - return 0; + /* characters supported everywhere in the name */ + if ( (name[i]>='@' && name[i] <= 'Z') || + (name[i]>='a' && name[i] <= 'z') || + (name[i]>='0' && name[i] <= '9') || + name[i]=='.' || name[i]=='_' || name[i]=='$' ) + continue; + /* characters that may be anywhere except as first character */ + if ( i>0 && ( name[i]=='-' || name[i]=='~' ) ) + continue; + /* characters that may not be the first or last character */ + if ( ( i>0 && name[i+1]!='\0' ) && ( name[i]=='\\' || name[i]==' ') ) + continue; + /* anything else is bad */ + return 0; } /* no test failed so it must be good */ return -1; diff --git a/nslcd/common.h b/nslcd/common.h index 90e9b10..5bd98ea 100644 --- a/nslcd/common.h +++ b/nslcd/common.h @@ -59,7 +59,8 @@ int mysnprintf(char *buffer,size_t buflen,const char *format, ...) /etc/group or /etc/shadow depending upon what is in the directory. This function will return NULL if no passwd is found and will return the literal value in the directory if conversion is not possible. */ -const char *get_userpassword(MYLDAP_ENTRY *entry,const char *attr); +const char *get_userpassword(MYLDAP_ENTRY *entry,const char *attr, + char *buffer,size_t buflen); /* write out an address, parsing the addr value */ int write_address(TFILE *fp,const char *addr); @@ -94,6 +95,9 @@ MYLDAP_ENTRY *uid2entry(MYLDAP_SESSION *session,const char *uid,int *rcp); /* transforms the uid into a DN by doing an LDAP lookup */ MUST_USE char *uid2dn(MYLDAP_SESSION *session,const char *uid,char *buf,size_t buflen); +/* try to update the shadowLastChange attribute of the entry if possible */ +int update_lastchange(MYLDAP_SESSION *session,const char *userdn); + /* these are the functions for initialising the database specific modules */ void alias_init(void); diff --git a/nslcd/group.c b/nslcd/group.c index baf367e..fa50d6f 100644 --- a/nslcd/group.c +++ b/nslcd/group.c @@ -61,7 +61,7 @@ const char *group_filter = "(objectClass=posixGroup)"; /* the attributes to request with searches */ const char *attmap_group_cn = "cn"; -const char *attmap_group_userPassword = "userPassword"; +const char *attmap_group_userPassword = "\"*\""; const char *attmap_group_gidNumber = "gidNumber"; const char *attmap_group_memberUid = "memberUid"; const char *attmap_group_uniqueMember = "uniqueMember"; @@ -69,9 +69,8 @@ const char *attmap_group_uniqueMember = "uniqueMember"; /* default values for attributes */ static const char *default_group_userPassword = "*"; /* unmatchable */ - /* the attribute list to request with searches */ -static const char *group_attrs[6]; +static const char **group_attrs=NULL; /* create a search filter for searching a group entry by name, return -1 on errors */ @@ -132,6 +131,7 @@ static int mkfilter_group_bymember(MYLDAP_SESSION *session, void group_init(void) { int i; + SET *set; /* set up search bases */ if (group_bases[0]==NULL) for (i=0;i<NSS_LDAP_CONFIG_MAX_BASES;i++) @@ -140,12 +140,14 @@ void group_init(void) if (group_scope==LDAP_SCOPE_DEFAULT) group_scope=nslcd_cfg->ldc_scope; /* set up attribute list */ - group_attrs[0]=attmap_group_cn; - group_attrs[1]=attmap_group_userPassword; - group_attrs[2]=attmap_group_memberUid; - group_attrs[3]=attmap_group_gidNumber; - group_attrs[4]=attmap_group_uniqueMember; - group_attrs[5]=NULL; + set=set_new(); + attmap_add_attributes(set,attmap_group_cn); + attmap_add_attributes(set,attmap_group_userPassword); + attmap_add_attributes(set,attmap_group_memberUid); + attmap_add_attributes(set,attmap_group_gidNumber); + attmap_add_attributes(set,attmap_group_uniqueMember); + group_attrs=set_tolist(set); + set_free(set); } static int do_write_group( @@ -224,6 +226,7 @@ static int write_group(TFILE *fp,MYLDAP_ENTRY *entry,const char *reqname, gid_t gids[MAXGIDS_PER_ENTRY]; int numgids; char *tmp; + char passbuffer[80]; int rc; /* get group name (cn) */ names=myldap_get_values(entry,attmap_group_cn); @@ -260,7 +263,7 @@ static int write_group(TFILE *fp,MYLDAP_ENTRY *entry,const char *reqname, } } /* get group passwd (userPassword) (use only first entry) */ - passwd=get_userpassword(entry,attmap_group_userPassword); + passwd=get_userpassword(entry,attmap_group_userPassword,passbuffer,sizeof(passbuffer)); if (passwd==NULL) passwd=default_group_userPassword; /* get group memebers (memberUid&uniqueMember) */ diff --git a/nslcd/myldap.c b/nslcd/myldap.c index fb0f617..a1acb21 100644 --- a/nslcd/myldap.c +++ b/nslcd/myldap.c @@ -1705,3 +1705,15 @@ int myldap_passwd( } return rc; } + +int myldap_modify(MYLDAP_SESSION *session,const char *dn,LDAPMod *mods[]) +{ + int rc; + if (!is_valid_session(session)||(dn==NULL)) + { + log_log(LOG_ERR,"myldap_passwd(): invalid parameter passed"); + errno=EINVAL; + return LDAP_OTHER; + } + return ldap_modify_ext_s(session->ld,dn,mods,NULL,NULL); +} diff --git a/nslcd/myldap.h b/nslcd/myldap.h index f7df4a3..e0fe688 100644 --- a/nslcd/myldap.h +++ b/nslcd/myldap.h @@ -139,9 +139,12 @@ MUST_USE int myldap_escape(const char *src,char *buffer,size_t buflen); /* Set the debug level globally. Returns an LDAP status code. */ int myldap_set_debuglevel(int i); -/* Perform an EXOP password modification call. */ +/* Perform an EXOP password modification call. Returns an LDAP status code. */ int myldap_passwd( MYLDAP_SESSION *session, const char *userdn,const char *oldpassword,const char *newpasswd); +/* Perform an LDAP modification request. Returns an LDAP status code. */ +int myldap_modify(MYLDAP_SESSION *session,const char *dn,LDAPMod *mods[]); + #endif /* not NSLCD__MYLDAP_H */ diff --git a/nslcd/pam.c b/nslcd/pam.c index f6d3877..5c1d0a8 100644 --- a/nslcd/pam.c +++ b/nslcd/pam.c @@ -457,6 +457,11 @@ static int try_pwmod(const char *binddn,const char *userdn, oldpassword=NULL; /* perform password modification */ rc=myldap_passwd(session,userdn,oldpassword,newpassword); + if (rc==LDAP_SUCCESS) + { + /* try to update the shadowLastChange attribute */ + (void)update_lastchange(session,userdn); + } } /* close the session */ myldap_session_close(session); diff --git a/nslcd/passwd.c b/nslcd/passwd.c index f0dceb0..9113f5d 100644 --- a/nslcd/passwd.c +++ b/nslcd/passwd.c @@ -56,7 +56,7 @@ const char *passwd_filter = "(objectClass=posixAccount)"; /* the attributes used in searches */ const char *attmap_passwd_uid = "uid"; -const char *attmap_passwd_userPassword = "userPassword"; +const char *attmap_passwd_userPassword = "\"*\""; const char *attmap_passwd_uidNumber = "uidNumber"; const char *attmap_passwd_gidNumber = "gidNumber"; const char *attmap_passwd_gecos = "\"${gecos:-$cn}\""; @@ -138,13 +138,46 @@ struct dn2uid_cache_entry }; #define DN2UID_CACHE_TIMEOUT (15*60) +/* checks whether the entry has a valid uidNumber attribute + (>= nss_min_uid) */ +static int entry_has_valid_uid(MYLDAP_ENTRY *entry) +{ + int i; + const char **values; + char *tmp; + uid_t uid; + /* if min_uid is not set any entry should do */ + if (nslcd_cfg->ldc_nss_min_uid==0) + return 1; + /* get all uidNumber attributes */ + values=myldap_get_values(entry,attmap_passwd_uidNumber); + if ((values==NULL)||(values[0]==NULL)) + { + log_log(LOG_WARNING,"passwd entry %s does not contain %s value", + myldap_get_dn(entry),attmap_passwd_uidNumber); + return 0; + } + /* check if there is a uidNumber attributes >= min_uid */ + for (i=0;values[i]!=NULL;i++) + { + uid=(uid_t)strtol(values[i],&tmp,0); + if ((*(values[i])=='\0')||(*tmp!='\0')) + log_log(LOG_WARNING,"passwd entry %s contains non-numeric %s value", + myldap_get_dn(entry),attmap_passwd_uidNumber); + else if (uid>=nslcd_cfg->ldc_nss_min_uid) + return 1; + } + /* nothing found */ + return 0; +} + /* Perform an LDAP lookup to translate the DN into a uid. This function either returns NULL or a strdup()ed string. */ char *lookup_dn2uid(MYLDAP_SESSION *session,const char *dn,int *rcp,char *buf,size_t buflen) { MYLDAP_SEARCH *search; MYLDAP_ENTRY *entry; - static const char *attrs[2]; + static const char *attrs[3]; int rc=LDAP_SUCCESS; const char **values; char *uid=NULL; @@ -152,7 +185,8 @@ char *lookup_dn2uid(MYLDAP_SESSION *session,const char *dn,int *rcp,char *buf,si rcp=&rc; /* we have to look up the entry */ attrs[0]=attmap_passwd_uid; - attrs[1]=NULL; + attrs[1]=attmap_passwd_uidNumber; + attrs[2]=NULL; search=myldap_search(session,dn,LDAP_SCOPE_BASE,passwd_filter,attrs,rcp); if (search==NULL) { @@ -166,13 +200,17 @@ char *lookup_dn2uid(MYLDAP_SESSION *session,const char *dn,int *rcp,char *buf,si log_log(LOG_WARNING,"lookup of user %s failed: %s",dn,ldap_err2string(*rcp)); return NULL; } - /* get uid (just use first one) */ - values=myldap_get_values(entry,attmap_passwd_uid); - /* check the result for presence and validity */ - if ((values!=NULL)&&(values[0]!=NULL)&&isvalidname(values[0])&&(strlen(values[0])<buflen)) + /* check the uidNumber attribute if min_uid is set */ + if (entry_has_valid_uid(entry)) { - strcpy(buf,values[0]); - uid=buf; + /* get uid (just use first one) */ + values=myldap_get_values(entry,attmap_passwd_uid); + /* check the result for presence and validity */ + if ((values!=NULL)&&(values[0]!=NULL)&&isvalidname(values[0])&&(strlen(values[0])<buflen)) + { + strcpy(buf,values[0]); + uid=buf; + } } /* clean up and return */ myldap_search_close(search); @@ -258,14 +296,15 @@ MYLDAP_ENTRY *uid2entry(MYLDAP_SESSION *session,const char *uid,int *rcp) MYLDAP_ENTRY *entry=NULL; const char *base; int i; - static const char *attrs[2]; + static const char *attrs[3]; char filter[1024]; /* if it isn't a valid username, just bail out now */ if (!isvalidname(uid)) return NULL; /* set up attributes (we don't need much) */ attrs[0]=attmap_passwd_uid; - attrs[1]=NULL; + attrs[1]=attmap_passwd_uidNumber; + attrs[2]=NULL; /* we have to look up the entry */ mkfilter_passwd_byname(uid,filter,sizeof(filter)); for (i=0;(i<NSS_LDAP_CONFIG_MAX_BASES)&&((base=passwd_bases[i])!=NULL);i++) @@ -274,7 +313,7 @@ MYLDAP_ENTRY *uid2entry(MYLDAP_SESSION *session,const char *uid,int *rcp) if (search==NULL) return NULL; entry=myldap_get_entry(search,NULL); - if (entry!=NULL) + if ((entry!=NULL)&&(entry_has_valid_uid(entry))) return entry; } return NULL; @@ -309,6 +348,7 @@ static int write_passwd(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser, char gecos[100]; char homedir[100]; char shell[100]; + char passbuffer[80]; int i,j; /* get the usernames for this entry */ usernames=myldap_get_values(entry,attmap_passwd_uid); @@ -326,7 +366,7 @@ static int write_passwd(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser, } else { - passwd=get_userpassword(entry,attmap_passwd_userPassword); + passwd=get_userpassword(entry,attmap_passwd_userPassword,passbuffer,sizeof(passbuffer)); if ((passwd==NULL)||(calleruid!=0)) passwd=default_passwd_userPassword; } @@ -393,14 +433,17 @@ static int write_passwd(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser, { for (j=0;j<numuids;j++) { - WRITE_INT32(fp,NSLCD_RESULT_BEGIN); - WRITE_STRING(fp,usernames[i]); - WRITE_STRING(fp,passwd); - WRITE_TYPE(fp,uids[j],uid_t); - WRITE_TYPE(fp,gid,gid_t); - WRITE_STRING(fp,gecos); - WRITE_STRING(fp,homedir); - WRITE_STRING(fp,shell); + if (uids[j]>=nslcd_cfg->ldc_nss_min_uid) + { + WRITE_INT32(fp,NSLCD_RESULT_BEGIN); + WRITE_STRING(fp,usernames[i]); + WRITE_STRING(fp,passwd); + WRITE_TYPE(fp,uids[j],uid_t); + WRITE_TYPE(fp,gid,gid_t); + WRITE_STRING(fp,gecos); + WRITE_STRING(fp,homedir); + WRITE_STRING(fp,shell); + } } } } @@ -427,7 +470,14 @@ NSLCD_HANDLE_UID( uid_t uid; char filter[1024]; READ_TYPE(fp,uid,uid_t); - log_setrequest("passwd=%d",(int)uid);, + log_setrequest("passwd=%d",(int)uid); + if (uid<nslcd_cfg->ldc_nss_min_uid) + { + /* return an empty result */ + WRITE_INT32(fp,NSLCD_VERSION); + WRITE_INT32(fp,NSLCD_ACTION_PASSWD_BYUID); + WRITE_INT32(fp,NSLCD_RESULT_END); + }, NSLCD_ACTION_PASSWD_BYUID, mkfilter_passwd_byuid(uid,filter,sizeof(filter)), write_passwd(fp,entry,NULL,&uid,calleruid) diff --git a/nslcd/shadow.c b/nslcd/shadow.c index a5c4a6d..e5f4a54 100644 --- a/nslcd/shadow.c +++ b/nslcd/shadow.c @@ -28,6 +28,7 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> +#include <time.h> #include "common.h" #include "log.h" @@ -54,7 +55,7 @@ const char *shadow_filter = "(objectClass=shadowAccount)"; /* the attributes to request with searches */ const char *attmap_shadow_uid = "uid"; -const char *attmap_shadow_userPassword = "userPassword"; +const char *attmap_shadow_userPassword = "\"*\""; const char *attmap_shadow_shadowLastChange = "\"${shadowLastChange:--1}\""; const char *attmap_shadow_shadowMin = "\"${shadowMin:--1}\""; const char *attmap_shadow_shadowMax = "\"${shadowMax:--1}\""; @@ -169,6 +170,71 @@ static long to_date(const char *date,const char *attr) tmpvalue=""; \ var=to_date(tmpvalue,attmap_shadow_##att); +/* try to update the shadowLastChange attribute of the entry if possible */ +int update_lastchange(MYLDAP_SESSION *session,const char *userdn) +{ + MYLDAP_SEARCH *search; + MYLDAP_ENTRY *entry; + static const char *attrs[3]; + const char *attr; + int rc; + const char **values; + LDAPMod mod,*mods[2]; + char buffer[80],*strvals[2]; + /* find the name of the attribute to use */ + if ( (attmap_shadow_shadowLastChange==NULL) || (attmap_shadow_shadowLastChange[0]=='\0') ) + return LDAP_LOCAL_ERROR; /* attribute not set at all */ + else if (strcmp(attmap_shadow_shadowLastChange,"\"${shadowLastChange:--1}\"")==0) + attr="shadowLastChange"; + else if (attmap_shadow_shadowLastChange[0]=='\"') + return LDAP_LOCAL_ERROR; /* other expressions not supported for now */ + else + attr=attmap_shadow_shadowLastChange; + /* set up the attributes we need */ + attrs[0]=attmap_shadow_uid; + attrs[1]=attr; + attrs[2]=NULL; + /* find the entry to see if the attribute is present */ + search=myldap_search(session,userdn,LDAP_SCOPE_BASE,shadow_filter,attrs,&rc); + if (search==NULL) + return rc; + entry=myldap_get_entry(search,&rc); + if (entry==NULL) + return rc; + values=myldap_get_values(entry,attr); + if ((values==NULL)||(values[0]==NULL)||(values[0][0]=='\0')) + return LDAP_NO_SUCH_ATTRIBUTE; + /* build the value for the new attribute */ + if (strcasecmp(attr,"pwdLastSet")==0) + { + /* for AD we use another timestamp */ + if(mysnprintf(buffer,sizeof(buffer),"%ld000000000",((long int)time(NULL)/100L+(134774L*864L)))) + return LDAP_LOCAL_ERROR; + } + else + { + /* time in days since Jan 1, 1970 */ + if(mysnprintf(buffer,sizeof(buffer),"%ld",((long int)(time(NULL)/(long int)(60*60*24))))) + return LDAP_LOCAL_ERROR; + } + /* update the shadowLastChange attribute */ + strvals[0]=buffer; + strvals[1]=NULL; + mod.mod_op=LDAP_MOD_REPLACE; + mod.mod_type=(char *)attr; + mod.mod_values=strvals; + mods[0]=&mod; + mods[1]=NULL; + rc=myldap_modify(session,userdn,mods); + if (rc!=LDAP_SUCCESS) + log_log(LOG_WARNING,"modification of %s attribute of %s failed: %s", + attr,userdn,ldap_err2string(rc)); + else + log_log(LOG_DEBUG,"modification of %s attribute of %s succeeded", + attr,userdn); + return rc; +} + static int write_shadow(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser) { int32_t tmpint32; @@ -185,6 +251,7 @@ static int write_shadow(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser) unsigned long flag; int i; char buffer[80]; + char passbuffer[80]; /* get username */ usernames=myldap_get_values(entry,attmap_shadow_uid); if ((usernames==NULL)||(usernames[0]==NULL)) @@ -194,7 +261,7 @@ static int write_shadow(TFILE *fp,MYLDAP_ENTRY *entry,const char *requser) return 0; } /* get password */ - passwd=get_userpassword(entry,attmap_shadow_userPassword); + passwd=get_userpassword(entry,attmap_shadow_userPassword,passbuffer,sizeof(passbuffer)); if (passwd==NULL) passwd=default_shadow_userPassword; /* get lastchange date */ diff --git a/pam/Makefile.am b/pam/Makefile.am index b374ca9..e2145ba 100644 --- a/pam/Makefile.am +++ b/pam/Makefile.am @@ -34,9 +34,9 @@ install-exec-local: install-pam_ldap_so uninstall-local: uninstall-pam_ldap_so install-pam_ldap_so: pam_ldap.so - -rm -f $(DESTDIR)/$(PAM_SECLIB_DIR)/$(PAM_LDAP_SONAME) - $(mkinstalldirs) $(DESTDIR)/$(PAM_SECLIB_DIR) + -rm -f $(DESTDIR)$(PAM_SECLIB_DIR)/$(PAM_LDAP_SONAME) + $(mkinstalldirs) $(DESTDIR)$(PAM_SECLIB_DIR) $(INSTALL_PROGRAM) pam_ldap.so $(DESTDIR)$(PAM_SECLIB_DIR)/$(PAM_LDAP_SONAME) uninstall-pam_ldap_so: - -rm -f $(DESTDIR)/$(PAM_SECLIB_DIR)/$(PAM_LDAP_SONAME) + -rm -f $(DESTDIR)$(PAM_SECLIB_DIR)/$(PAM_LDAP_SONAME) diff --git a/py-compile b/py-compile new file mode 100755 index 0000000..3f9d05b --- /dev/null +++ b/py-compile @@ -0,0 +1,146 @@ +#!/bin/sh +# py-compile - Compile a Python program + +scriptversion=2009-04-28.21; # UTC + +# Copyright (C) 2000, 2001, 2003, 2004, 2005, 2008, 2009 Free Software +# Foundation, Inc. + +# 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, 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, see <http://www.gnu.org/licenses/>. + +# As a special exception to the GNU General Public License, if you +# distribute this file as part of a program that contains a +# configuration script generated by Autoconf, you may include it under +# the same distribution terms that you use for the rest of that program. + +# This file is maintained in Automake, please report +# bugs to <bug-automake@gnu.org> or send patches to +# <automake-patches@gnu.org>. + +if [ -z "$PYTHON" ]; then + PYTHON=python +fi + +basedir= +destdir= +files= +while test $# -ne 0; do + case "$1" in + --basedir) + basedir=$2 + if test -z "$basedir"; then + echo "$0: Missing argument to --basedir." 1>&2 + exit 1 + fi + shift + ;; + --destdir) + destdir=$2 + if test -z "$destdir"; then + echo "$0: Missing argument to --destdir." 1>&2 + exit 1 + fi + shift + ;; + -h|--h*) + cat <<\EOF +Usage: py-compile [--help] [--version] [--basedir DIR] [--destdir DIR] FILES..." + +Byte compile some python scripts FILES. Use --destdir to specify any +leading directory path to the FILES that you don't want to include in the +byte compiled file. Specify --basedir for any additional path information you +do want to be shown in the byte compiled file. + +Example: + py-compile --destdir /tmp/pkg-root --basedir /usr/share/test test.py test2.py + +Report bugs to <bug-automake@gnu.org>. +EOF + exit $? + ;; + -v|--v*) + echo "py-compile $scriptversion" + exit $? + ;; + *) + files="$files $1" + ;; + esac + shift +done + +if test -z "$files"; then + echo "$0: No files given. Try \`$0 --help' for more information." 1>&2 + exit 1 +fi + +# if basedir was given, then it should be prepended to filenames before +# byte compilation. +if [ -z "$basedir" ]; then + pathtrans="path = file" +else + pathtrans="path = os.path.join('$basedir', file)" +fi + +# if destdir was given, then it needs to be prepended to the filename to +# byte compile but not go into the compiled file. +if [ -z "$destdir" ]; then + filetrans="filepath = path" +else + filetrans="filepath = os.path.normpath('$destdir' + os.sep + path)" +fi + +$PYTHON -c " +import sys, os, py_compile + +files = '''$files''' + +sys.stdout.write('Byte-compiling python modules...\n') +for file in files.split(): + $pathtrans + $filetrans + if not os.path.exists(filepath) or not (len(filepath) >= 3 + and filepath[-3:] == '.py'): + continue + sys.stdout.write(file) + sys.stdout.flush() + py_compile.compile(filepath, filepath + 'c', path) +sys.stdout.write('\n')" || exit $? + +# this will fail for python < 1.5, but that doesn't matter ... +$PYTHON -O -c " +import sys, os, py_compile + +files = '''$files''' +sys.stdout.write('Byte-compiling python modules (optimized versions) ...\n') +for file in files.split(): + $pathtrans + $filetrans + if not os.path.exists(filepath) or not (len(filepath) >= 3 + and filepath[-3:] == '.py'): + continue + sys.stdout.write(file) + sys.stdout.flush() + py_compile.compile(filepath, filepath + 'o', path) +sys.stdout.write('\n')" 2>/dev/null || : + +# Local Variables: +# mode: shell-script +# sh-indentation: 2 +# eval: (add-hook 'write-file-hooks 'time-stamp) +# time-stamp-start: "scriptversion=" +# time-stamp-format: "%:y-%02m-%02d.%02H" +# time-stamp-time-zone: "UTC" +# time-stamp-end: "; # UTC" +# End: diff --git a/pynslcd/Makefile.am b/pynslcd/Makefile.am new file mode 100644 index 0000000..098cdfb --- /dev/null +++ b/pynslcd/Makefile.am @@ -0,0 +1,41 @@ +# Makefile.am - use automake to generate Makefile.in +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +pynslcddir = $(datadir)/pynslcd + +pynslcd_PYTHON = pynslcd.py cfg.py common.py tio.py \ + ether.py group.py passwd.py +nodist_pynslcd_PYTHON = constants.py config.py +CLEANFILES = $(nodist_pynslcd_PYTHON) + +all-local: $(nodist_pynslcd_PYTHON) + +# create a symbolic link for the pynslcd daemon and fix permissions +install-data-hook: + chmod a+rx $(DESTDIR)$(pynslcddir)/pynslcd.py + $(MKDIR_P) $(DESTDIR)$(sbindir) + [ -L $(DESTDIR)$(sbindir)/pynslcd ] || $(LN_S) $(pynslcddir)/pynslcd.py $(DESTDIR)$(sbindir)/pynslcd + +# generate constants module +constants.py: $(top_srcdir)/nslcd.h Makefile + ( echo "# This file is automatically generated from nslcd.h." ; \ + echo "# See that file for details." ; \ + echo "" ; \ + sed -n 's| */\*.*\*/ *||;s/^.define *\(NSLCD_[A-Z_]*\) */\1 = /p' \ + $< ) > $@ diff --git a/pynslcd/alias.py b/pynslcd/alias.py new file mode 100644 index 0000000..5126466 --- /dev/null +++ b/pynslcd/alias.py @@ -0,0 +1,74 @@ + +# alias.py - lookup functions for aliasnet addresses +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import constants +import common + +import ldap +import ldap.filter + + +class AliasRequest(common.Request): + + filter = '(objectClass=nisMailAlias)' + + attmap_cn = 'cn' + attmap_rfc822MailMember = 'rfc822MailMember' + + attributes = ( 'cn', 'rfc822MailMember' ) + + def write(self, entry): + dn, attributes = entry + # get name and check against requested name + names = attributes.get(self.attmap_cn, []) + if not names: + logging.error('Error: entry %s does not contain %s value', dn, self.attmap_cn) + return + if self.name: + if self.name.lower() not in (x.lower() for x in names): + return + names = ( self.name, ) + # get the members of the alias + members = attributes.get(self.attmap_rfc822MailMember, []) + if not members: + logging.error('Error: entry %s does not contain %s value', dn, self.attmap_rfc822MailMember) + return + # write results + for name in names: + self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) + self.fp.write_string(name) + self.fp.write_stringlist(members) + + +class AliasByNameRequest(AliasRequest): + + action = constants.NSLCD_ACTION_ALIAS_BYNAME + + def read_parameters(self): + self.name = self.fp.read_string() + + def mk_filter(self): + return '(&%s(%s=%s))' % ( self.filter, + self.attmap_cn, ldap.filter.escape_filter_chars(self.name) ) + + +class AliasAllRequest(AliasRequest): + + action = constants.NSLCD_ACTION_ALIAS_ALL diff --git a/pynslcd/cfg.py b/pynslcd/cfg.py new file mode 100644 index 0000000..000e601 --- /dev/null +++ b/pynslcd/cfg.py @@ -0,0 +1,58 @@ + +# cfg.py - module for accessing configuration information +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import ldap + +# these values are defined here + +# the name of the program +program_name = 'pynslcd' +# the debugging level +debug = 0 +# whether the --check option was passed +check = False +# the number of threads to start +threads = 5 + +# the user id nslcd should be run as +uid = None +# the group id nslcd should be run as +gid = None + +# the LDAP server to use +# FIXME: support multiple servers and have a fail-over mechanism +ldap_uri = 'ldapi:///' + +# default search scope for searches +scope = ldap.SCOPE_SUBTREE + +# LDAP search bases to search +bases = ( 'dc=test, dc=tld', ) + +# the users for which no initgroups() searches should be done +nss_initgroups_ignoreusers = [] + +# the DN to use to perform password modifications as root +rootpwmoddn = 'cn=admin, dc=test, dc=tld' +rootpwmodpw = 'test' + +# FIXME: implement reading configuration from file +def read(cfgfile): + pass diff --git a/pynslcd/common.py b/pynslcd/common.py new file mode 100644 index 0000000..fd3196a --- /dev/null +++ b/pynslcd/common.py @@ -0,0 +1,125 @@ + +# common.py - functions that are used by different modules +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import cfg +import constants + +import re +import ldap +import ldap.dn + +_validname_re = re.compile(r'^[A-Za-z0-9._@$][A-Za-z0-9._@$ \\~-]{0,98}[A-Za-z0-9._@$~-]$') + +def isvalidname(name): + """Checks to see if the specified name seems to be a valid user or group + name. + + This test is based on the definition from POSIX (IEEE Std 1003.1, 2004, + 3.426 User Name, 3.189 Group Name and 3.276 Portable Filename Character Set): + http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_426 + http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_189 + http://www.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap03.html#tag_03_276 + + The standard defines user names valid if they contain characters from + the set [A-Za-z0-9._-] where the hyphen should not be used as first + character. As an extension this test allows some more characters.""" + return bool(_validname_re.match(name)) + +def validate_name(name): + """Checks to see if the specified name seems to be a valid user or group + name. See isvalidname().""" + if not _validname_re.match(name): + raise ValueError('%r: invalid user name' % name) + + +class Request(object): + """ + Request handler class. Subclasses are expected to handle actual requests + and should implement the following members: + + action: the NSLCD_ACTION_* action that should trigger this handler + read_parameters: a function that reads the request parameters of the + request stream + filter: LDAP search filter + mk_filter (optional): function that returns the LDAP search filter + write: function that writes a single LDAP entry to the result stream + """ + + bases = cfg.bases + scope = cfg.scope + + def __init__(self, fp, conn, calleruid): + self.fp = fp + self.conn = conn + self.calleruid = calleruid + # have default empty values for these + self.name = None + self.uid = None + self.gid = None + self.address = None + + def read_parameters(self): + """This method should read the parameters from ths stream and + store them in self.""" + pass + + def mk_filter(self): + """Return the active search filter (based on the read parameters).""" + return self.filter + + def handle_request(self): + """This method handles the request based on the parameters read + with read_parameters().""" + # get search results + for base in self.bases: + # do the LDAP search + try: + res = self.conn.search_s(base, self.scope, self.mk_filter(), self.attributes) + for entry in res: + if entry[0]: + self.write(entry) + except ldap.NO_SUCH_OBJECT: + # FIXME: log message + pass + # write the final result code + self.fp.write_int32(constants.NSLCD_RESULT_END) + + def __call__(self): + self.read_parameters() + # TODO: log call with parameters + self.fp.write_int32(constants.NSLCD_VERSION) + self.fp.write_int32(self.action) + self.handle_request() + + +def get_handlers(module): + """Return a dictionary mapping actions to Request classes.""" + import inspect + res = {} + if isinstance(module, basestring): + module = __import__(module, globals()) + for name, cls in inspect.getmembers(module, inspect.isclass): + if issubclass(cls, Request) and hasattr(cls, 'action'): + res[cls.action] = cls + return res + +def get_rdn_value(entry, attribute): + dn, attributes = entry + return dict((x, y) for x, y, z in ldap.dn.str2dn(dn)[0])[attribute] diff --git a/pynslcd/config.py.in b/pynslcd/config.py.in new file mode 100644 index 0000000..bab172e --- /dev/null +++ b/pynslcd/config.py.in @@ -0,0 +1,61 @@ + +# config.py.in - configured information, this file is processed by the +# configure script to produce config.py +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + + +# Name of package +PACKAGE = '''@PACKAGE@''' + +# Define to the address where bug reports for this package should be sent. +PACKAGE_BUGREPORT = '''@PACKAGE_BUGREPORT@''' + +# Define to the full name of this package. +PACKAGE_NAME = '''@PACKAGE_NAME@''' + +# Define to the full name and version of this package. +PACKAGE_STRING = '''@PACKAGE_STRING@''' + +# Define to the one symbol short name of this package. +PACKAGE_TARNAME = '''@PACKAGE_TARNAME@''' + +# Define to the home page for this package. +PACKAGE_URL = '''@PACKAGE_URL@''' + +# Define to the version of this package. +PACKAGE_VERSION = '''@PACKAGE_VERSION@''' + +# Version number of package +VERSION = '''@VERSION@''' + +# Whether to check configfile options. +ENABLE_CONFIGFILE_CHECKING = '''@ENABLE_CONFIGFILE_CHECKING@''' + +# Path to bindpw value. +NSLCD_BINDPW_PATH = '''@NSLCD_BINDPW_PATH@''' + +# Path to nslcd configuration file. +NSLCD_CONF_PATH = '''@NSLCD_CONF_PATH@''' + +# The location of the pidfile used for checking availability of the nslcd. +NSLCD_PIDFILE = '''@NSLCD_PIDFILE@''' + +# The location of the socket used for communicating. +NSLCD_SOCKET = '''@NSLCD_SOCKET@''' + +# The SONAME of the NSS library module. +NSS_LDAP_SONAME = '''@NSS_LDAP_SONAME@''' diff --git a/pynslcd/debugio.py b/pynslcd/debugio.py new file mode 100644 index 0000000..dbb5b02 --- /dev/null +++ b/pynslcd/debugio.py @@ -0,0 +1,65 @@ + +# debugio.py - module for debugging an I/O stream +# +# Copyright (C) 2008, 2009 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +class DebugIO(): + """This class is a file-like object that writes from one file and + writes to another. It is mainly used for debugging the serial protocol + without a serial connection.""" + + def __init__(self, name): + import os + if not os.path.exists(name+'.in'): os.mkfifo(name+'.in') + if not os.path.exists(name+'.out'): os.mkfifo(name+'.out') + r = open(name+'.in', 'r', 0) + w = open(name+'.out', 'w', 0) + self._r = r + self._w = w + self.write = w.write + self.portstr = 'debuging to %s.in and %s.out' % ( name, name ) + self._timeout = None + + def close(self): + self._r.close() + self._w.close() + + def inWaiting(self): + # we are never out of data and 100 should be enough for everybody + return 100 + + def setTimeout(self, seconds): + self._timeout = seconds + + def getTimeout(self): + return self._timeout + + def read(self, size): + import select + read = '' + if size > 0: + while len(read) < size: + ready, _, _ = select.select([self._r.fileno()], [], [], self._timeout) + if not ready: + break #timeout + buf = self._r.read(size-len(read)) + read = read + buf + if self._timeout >= 0 and not buf: + break #early abort on timeout + return read + diff --git a/pynslcd/ether.py b/pynslcd/ether.py new file mode 100644 index 0000000..0995377 --- /dev/null +++ b/pynslcd/ether.py @@ -0,0 +1,100 @@ + +# ether.py - lookup functions for ethernet addresses +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import constants +import common + +import struct +import ldap.filter + + +def ether_aton(ether): + return struct.pack('BBBBBB', *(int(x, 16) for x in ether.split(':'))) + +def ether_ntoa(ether): + return ':'.join('%x' % x for x in struct.unpack('6B', ether)) + + +class EtherRequest(common.Request): + + filter = '(objectClass=ieee802Device)' + + attmap_cn = 'cn' + attmap_macAddress = 'macAddress' + + attributes = ( 'cn', 'macAddress' ) + + def __init__(self, *args): + super(EtherRequest, self).__init__(*args) + self.ether = None + + def write(self, entry): + dn, attributes = entry + # get name and check against requested user name + names = attributes.get(self.attmap_cn, []) + if not names: + print 'Error: entry %s does not contain %s value' % ( dn, self.attmap_cn) + if self.name: + if self.name.lower() not in (x.lower() for x in names): + return # skip entry + names = ( self.name, ) + # get addresses and convert to binary form + addresses = [ether_aton(x) for x in attributes.get(self.attmap_macAddress, [])] + if not addresses: + print 'Error: entry %s does not contain %s value' % ( dn, self.attmap_macAddress) + if self.ether: + if self.ether not in addresses: + return + addresses = ( self.ether, ) + # write results + for name in names: + for ether in addresses: + self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) + self.fp.write_string(name) + self.fp.write(ether) + + +class EtherByNameRequest(EtherRequest): + + action = constants.NSLCD_ACTION_ETHER_BYNAME + + def read_parameters(self): + self.name = self.fp.read_string() + + def mk_filter(self): + return '(&%s(%s=%s))' % ( self.filter, + self.attmap_cn, ldap.filter.escape_filter_chars(self.name) ) + + +class EtherByEtherRequest(EtherRequest): + + action = constants.NSLCD_ACTION_ETHER_BYETHER + + def read_parameters(self): + self.ether = self.fp.read(6) + + def mk_filter(self): + return '(&%s(%s=%s))' % ( self.filter, + self.attmap_macAddress, ether_ntoa(self.ether) ) + + +class EtherAllRequest(EtherRequest): + + action = constants.NSLCD_ACTION_ETHER_ALL diff --git a/pynslcd/group.py b/pynslcd/group.py new file mode 100644 index 0000000..4f85441 --- /dev/null +++ b/pynslcd/group.py @@ -0,0 +1,174 @@ + +# group.py - group entry lookup routines +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import constants +import common +import cfg + +import logging +import ldap +import ldap.filter + + +def clean(lst): + for i in lst: + yield i.replace('\0', '') + +class GroupRequest(common.Request): + + filter = '(|(objectClass=posixGroup)(objectClass=groupOfUniqueNames))' + + attmap_group_cn = 'cn' + attmap_group_userPassword = 'userPassword' + attmap_group_gidNumber = 'gidNumber' + attmap_group_memberUid = 'memberUid' + attmap_group_uniqueMember = 'uniqueMember' + + attributes = ( 'cn', 'userPassword', 'gidNumber', 'memberUid', + 'uniqueMember' ) + + wantmembers = True + + def write(self, entry): + dn, attributes = entry + # get uid attribute and check against requested user name + names = attributes.get('uid', []) + if self.name: + if self.name not in names: + return + names = ( self.name, ) + # get user password entry + passwd = '*' + # get numeric user and group ids + uids = ( self.uid, ) if self.uid else attributes.get(self.attmap_group_uidNumber, []) + uids = [ int(x) for x in uids ] + ( gid, ) = attributes[self.attmap_group_gidNumber] + gid = int(gid) + # FIXME: use expression here + gecos = attributes.get(self.attmap_group_gecos, [None])[0] or attributes.get(self.attmap_group_cn, [''])[0] + ( home, ) = attributes.get(self.attmap_group_homeDirectory, ['']) + ( shell, ) = attributes.get(self.attmap_group_loginShell, ['']) + for name in names: + if not common.isvalidname(name): + print 'Warning: group entry %s contains invalid user name: "%s"' % ( dn, name ) + else: + for uid in uids: + self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) + self.fp.write_string(name) + self.fp.write_string(passwd) + self.fp.write_uid_t(uid) + self.fp.write_gid_t(gid) + self.fp.write_string(gecos) + self.fp.write_string(home) + self.fp.write_string(shell) + + def write(self, entry): + dn, attributes = entry + # get group names and check against requested group name + names = attributes.get(self.attmap_group_cn, []) + if self.name: + if self.name not in names: + return + names = ( self.name, ) + # get group group password + ( passwd, ) = attributes.get(self.attmap_group_userPassword, ['*']) + # get group id(s) + gids = ( self.gid, ) if self.gid else attributes.get(self.attmap_group_gidNumber, []) + gids = [ int(x) for x in gids ] + # build member list + members = set() + if self.wantmembers: + # add the memberUid values + for member in clean(attributes.get(self.attmap_group_memberUid, [])): + #print 'found member %r' % member + if common.isvalidname(member): + members.add(member) + # translate and add the uniqueMember values + from passwd import dn2uid + for memberdn in clean(attributes.get(self.attmap_group_uniqueMember, [])): + member = dn2uid(self.conn, memberdn) + #print 'found memberdn %r, member=%r' % ( memberdn, member) + if member: + members.add(member) + # actually return the results + for name in names: + if not common.isvalidname(name): + print 'Warning: group entry %s contains invalid group name: "%s"' % ( dn, name ) + else: + for gid in gids: + self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) + self.fp.write_string(name) + self.fp.write_string(passwd) + self.fp.write_gid_t(gid) + self.fp.write_stringlist(members) + + +class GroupByNameRequest(GroupRequest): + + action = constants.NSLCD_ACTION_GROUP_BYNAME + + def read_parameters(self): + self.name = self.fp.read_string() + common.validate_name(self.name) + + def mk_filter(self): + return '(&%s(%s=%s))' % ( self.filter, + self.attmap_group_cn, ldap.filter.escape_filter_chars(self.name) ) + + +class GroupByGidRequest(GroupRequest): + + action = constants.NSLCD_ACTION_GROUP_BYGID + + def read_parameters(self): + self.gid = self.fp.read_gid_t() + + def mk_filter(self): + return '(&%s(%s=%d))' % ( self.filter, + self.attmap_group_gidNumber, self.gid ) + + +class GroupByMemberRequest(GroupRequest): + + action = constants.NSLCD_ACTION_GROUP_BYMEMBER + wantmembers = False + attributes = ( 'cn', 'userPassword', 'gidNumber' ) + + def read_parameters(self): + self.memberuid = self.fp.read_string() + common.validate_name(self.memberuid) + + def mk_filter(self): + # try to translate uid to DN + # TODO: only do this if memberuid attribute is mapped + import passwd + dn = passwd.uid2dn(self.conn, self.memberuid) + if dn: + return '(&%s(|(%s=%s)(%s=%s)))' % ( self.filter, + self.attmap_group_memberUid, ldap.filter.escape_filter_chars(self.memberuid), + self.attmap_group_uniqueMember, ldap.filter.escape_filter_chars(dn) ) + else: + return '(&%s(%s=%s))' % ( self.filter, + self.attmap_group_memberUid, ldap.filter.escape_filter_chars(self.memberuid) ) + + +class GroupAllRequest(GroupRequest): + + action = constants.NSLCD_ACTION_GROUP_ALL diff --git a/pynslcd/mypidfile.py b/pynslcd/mypidfile.py new file mode 100644 index 0000000..4781089 --- /dev/null +++ b/pynslcd/mypidfile.py @@ -0,0 +1,70 @@ + +# mypidfile.py - functions for properly locking a PIDFile +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import fcntl +import errno +import os + + +class MyPIDLockFile(object): + """Implementation of a PIDFile fit for use with the daemon module + that locks the PIDFile with fcntl.lockf().""" + + def __init__(self, path): + self.path = path + + def __enter__(self): + """Lock the PID file and write the process ID to the file.""" + fd = os.open(self.path, os.O_RDWR | os.O_CREAT, 0644) + try: + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + pidfile = os.fdopen(fd, 'w') + except: + os.close(fd) + raise + pidfile.write('%d\n' % os.getpid()) + pidfile.flush() + self.pidfile = pidfile + return self + + def __exit__(self, exc_type, exc_value, traceback): + """Release the lock (close the lockfile).""" + fcntl.lockf(self.pidfile.fileno(), fcntl.LOCK_UN) + self.pidfile.close() + del self.pidfile + + def is_locked(self): + """Check whether the file is already present and locked.""" + try: + fd = os.open(self.path, os.O_RDWR, 0644) + # Python doesn't seem to have F_TEST so we'll just try to lock + fcntl.lockf(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + # if we're here we must have aquired the lock + fcntl.lockf(fd, fcntl.LOCK_UN) + return False + except (IOError, OSError), e: + if e.errno == errno.ENOENT: + return False + if e.errno in (errno.EACCES, errno.EAGAIN): + return True + raise + finally: + if 'fd' in locals(): + os.close(fd) diff --git a/pynslcd/pam.py b/pynslcd/pam.py new file mode 100644 index 0000000..852830c --- /dev/null +++ b/pynslcd/pam.py @@ -0,0 +1,129 @@ + +# pam.py - functions authentication, authorisation and session handling +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import constants +import common +import cfg + +import logging +import ldap + +import passwd + +def try_bind(userdn, password): + # open a new connection + conn = ldap.initialize(cfg.ldap_uri) + # bind using the specified credentials + conn.simple_bind_s(userdn, password) + # perform search for own object (just to do any kind of search) + res = conn.search_s(userdn, ldap.SCOPE_BASE, '(objectClass=*)', [ 'dn', ]) + for entry in res: + if entry[0] == userdn: + return + raise ldap.NO_SUCH_OBJECT() + + +class PAMRequest(common.Request): + + def validate_request(self): + """This method checks the provided username for validity and fills + in the DN if needed.""" + from passwd import PasswdRequest + # check username for validity + common.validate_name(self.username) + # look up user DN if not known + if not self.userdn: + entry = passwd.uid2entry(self.conn, self.username) + if not entry: + raise ValueError('%r: user not found' % self.username) + # save the DN + self.userdn = entry[0] + # get the "real" username + value = common.get_rdn_value(entry, PasswdRequest.attmap_passwd_uid) + if not value: + # get the username from the uid attribute + values = myldap_get_values(entry, PasswdRequest.attmap_passwd_uid) + if not values or not values[0]: + logging.warn('%s: is missing a %s attribute', entry.dn, PasswdRequest.attmap_passwd_uid) + value = values[0] + # check the username + if value and not common.isvalidname(value): + raise ValueError('%s: has invalid %s attribute', entry.dn, PasswdRequest.attmap_passwd_uid) + # check if the username is different and update it if needed + if value != self.username: + logging.info('username changed from %r to %r', self.username, value) + self.username = value + + +class PAMAuthenticationRequest(PAMRequest): + + action = constants.NSLCD_ACTION_PAM_AUTHC + + def read_parameters(self): + self.username = self.fp.read_string() + self.userdn = self.fp.read_string() + self.servicename = self.fp.read_string() + self.password = self.fp.read_string() + #self.validate_request() + # TODO: log call with parameters + + def write(self, code=constants.NSLCD_PAM_SUCCESS, msg=''): + self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) + self.fp.write_string(self.username) + self.fp.write_string(self.userdn) + self.fp.write_int32(code) # authc + self.fp.write_int32(constants.NSLCD_PAM_SUCCESS) # authz + self.fp.write_string(msg) # authzmsg + self.fp.write_int32(constants.NSLCD_RESULT_END) + + def handle_request(self): + # if the username is blank and rootpwmoddn is configured, try to + # authenticate as administrator, otherwise validate request as usual + if not self.username and cfg.ldc_rootpwmoddn: + # authenticate as rootpwmoddn + self.userdn = cfg.ldc_rootpwmoddn + # if the caller is root we will allow the use of rootpwmodpw + if not self.password and self.calleruid == 0 and cfg.rootpwmodpw: + self.password = cfg.rootpwmodpw + else: + self.validate_request() + # try authentication + try: + try_bind(self.userdn, self.password) + logging.debug('bind successful') + self.write() + except ldap.INVALID_CREDENTIALS, e: + try: + msg = e[0]['desc'] + except: + msg = str(e) + logging.debug('bind failed: %s', msg) + self.write(constants.NSLCD_PAM_AUTH_ERR, msg) + +#class PAMAuthorisationRequest(PAMRequest): + +# action = constants.NSLCD_ACTION_PAM_AUTHZ + +# def handle_request(self): + + +#NSLCD_ACTION_PAM_SESS_O +#NSLCD_ACTION_PAM_SESS_C +#NSLCD_ACTION_PAM_PWMOD diff --git a/pynslcd/passwd.py b/pynslcd/passwd.py new file mode 100644 index 0000000..80b2a4a --- /dev/null +++ b/pynslcd/passwd.py @@ -0,0 +1,163 @@ + +# passwd.py - lookup functions for user account information +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import constants +import common +import cfg + +import logging +import ldap +import ldap.filter + + +class PasswdRequest(common.Request): + + attmap = { 'uid': 'uid', 'userPassword': 'userPassword', + 'uidNumber': 'uidNumber', 'gidNumber': 'gidNumber', + 'gecos': '"${gecos:-$cn}"', 'cn': 'cn', + 'homeDirectory': 'homeDirectory', + 'loginShell': 'loginShell', + 'objectClass': 'objectClass' } + filter = '(objectClass=posixAccount)' + + attmap_passwd_uid = 'uid' + attmap_passwd_userPassword = 'userPassword' + attmap_passwd_uidNumber = 'uidNumber' + attmap_passwd_gidNumber = 'gidNumber' + attmap_passwd_gecos = '"${gecos:-$cn}"' + attmap_passwd_homeDirectory = 'homeDirectory' + attmap_passwd_loginShell = 'loginShell' + + # these should be removed + attmap_passwd_cn = 'cn' + + attributes = ( 'uid', 'userPassword', 'uidNumber', 'gidNumber', + 'gecos', 'cn', 'homeDirectory', 'loginShell', + 'objectClass' ) + + bases = ( 'ou=people,dc=test,dc=tld', ) + + def write(self, entry): + dn, attributes = entry + # get uid attribute and check against requested user name + names = attributes.get('uid', []) + if self.name: + if self.name not in names: + return + names = ( self.name, ) + # get user password entry + if 'shadowAccount' in attributes.get('objectClass', []): + passwd = 'x' + else: + passwd = '*'; + # get numeric user and group ids + uids = ( self.uid, ) if self.uid else attributes.get(self.attmap_passwd_uidNumber, []) + uids = [ int(x) for x in uids ] + ( gid, ) = attributes[self.attmap_passwd_gidNumber] + gid = int(gid) + # FIXME: use expression here + gecos = attributes.get(self.attmap_passwd_gecos, [None])[0] or attributes.get(self.attmap_passwd_cn, [''])[0] + ( home, ) = attributes.get(self.attmap_passwd_homeDirectory, ['']) + ( shell, ) = attributes.get(self.attmap_passwd_loginShell, ['']) + for name in names: + if not common.isvalidname(name): + print 'Warning: passwd entry %s contains invalid user name: "%s"' % ( dn, name ) + else: + for uid in uids: + #print '%s:%s:%d:%d:%s:%s:%s' % ( name, passwd, uid, gid, gecos, home, shell ) + self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) + self.fp.write_string(name) + self.fp.write_string(passwd) + self.fp.write_uid_t(uid) + self.fp.write_gid_t(gid) + self.fp.write_string(gecos) + self.fp.write_string(home) + self.fp.write_string(shell) + + +class PasswdByNameRequest(PasswdRequest): + + action = constants.NSLCD_ACTION_PASSWD_BYNAME + + def read_parameters(self): + self.name = self.fp.read_string() + common.validate_name(self.name) + + def mk_filter(self): + return '(&%s(%s=%s))' % ( self.filter, + self.attmap_passwd_uid, ldap.filter.escape_filter_chars(self.name) ) + + +class PasswdByUidRequest(PasswdRequest): + + action = constants.NSLCD_ACTION_PASSWD_BYUID + + def read_parameters(self): + self.uid = self.fp.read_uid_t() + + def mk_filter(self): + return '(&%s(%s=%d))' % ( self.filter, + self.attmap_passwd_uidNumber, self.uid ) + + +class PasswdAllRequest(PasswdRequest): + + action = constants.NSLCD_ACTION_PASSWD_ALL + + +def do_search(conn, filter=None, base=None): + mybases = ( base, ) if base else PasswdRequest.bases + filter = filter or PasswdRequest.filter + # perform a search for each search base + for base in mybases: + # do the LDAP search + try: + res = conn.search_s(base, PasswdRequest.scope, filter, [PasswdRequest.attmap_passwd_uid]) + for entry in res: + if entry[0]: + yield entry + except ldap.NO_SUCH_OBJECT: + # FIXME: log message + pass + +def uid2entry(conn, uid): + """Look up the user by uid and return the LDAP entry or None if the user + was not found.""" + myfilter = '(&%s(%s=%s))' % ( PasswdRequest.filter, + PasswdRequest.attmap_passwd_uid, ldap.filter.escape_filter_chars(uid) ) + for dn, attributes in do_search(conn, myfilter): + if uid in attributes[PasswdRequest.attmap_passwd_uid]: + return dn, attributes + +def uid2dn(conn, uid): + """Look up the user by uid and return the DN or None if the user was + not found.""" + x = uid2entry(conn, uid) + if x is not None: + return x[0] + +def dn2uid(conn, dn): + """Look up the user by dn and return a uid or None if the user was + not found.""" + try: + for dn, attributes in do_search(conn, base=dn): + return attributes[PasswdRequest.attmap_passwd_uid][0] + except ldap.NO_SUCH_OBJECT: + return None diff --git a/pynslcd/pynslcd.py b/pynslcd/pynslcd.py new file mode 100755 index 0000000..4644a35 --- /dev/null +++ b/pynslcd/pynslcd.py @@ -0,0 +1,276 @@ +#!/usr/bin/env python + +# pynslcd.py - main daemon module +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import os +import sys +import daemon +import mypidfile +import threading +import logging +import logging.handlers +import signal +import ldap + +import constants # from nslcd.h +import config # from configure +import cfg # from nslcd.conf +import common + +from tio import TIOStream + + +# configure logging +class MyFormatter(logging.Formatter): + def format(self, record): + msg = logging.Formatter.format(self, record) + if record.levelno == logging.DEBUG: + msg = 'DEBUG: %s' % msg + return msg +#logging.basicConfig(level=logging.INFO) +# , format='%(message)s' +formatter = MyFormatter('%(message)s') +stderrhandler = logging.StreamHandler(sys.stderr) +stderrhandler.setFormatter(formatter) +##sysloghandler = logging.handlers.SysLogHandler(address='/dev/log') +##sysloghandler.setFormatter(formatter) +#logging.getLogger().setFormatter(MyFormatter()) +logging.getLogger().addHandler(stderrhandler) + +#logger = logging.getLogger() +#logger.setLevel(logging.INFO) +#syslog = logging.handlers.SysLogHandler(address='/dev/log') +#formatter = logging.Formatter('%(name)s: %(levelname)s %(message)s') +#syslog.setFormatter(formatter) +#logger.addHandler(syslog) + +def display_version(fp): + fp.write('%(PACKAGE_STRING)s\n' + 'Written by Arthur de Jong.\n' + '\n' + 'Copyright (C) 2010 Arthur de Jong\n' + 'This is free software; see the source for copying conditions. There is NO\n' + 'warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.\n' + % { 'PACKAGE_STRING': config.PACKAGE_STRING, } ); + +def display_usage(fp): + fp.write("Usage: %(program_name)s [OPTION]...\n" + "Name Service LDAP connection daemon.\n" + " -c, --check check if the daemon already is running\n" + " -d, --debug don't fork and print debugging to stderr\n" + " --help display this help and exit\n" + " --version output version information and exit\n" + "\n" + "Report bugs to <%(PACKAGE_BUGREPORT)s>.\n" + % { 'program_name': cfg.program_name, + 'PACKAGE_BUGREPORT': config.PACKAGE_BUGREPORT, } ) + +def parse_cmdline(): + """Parse command-line arguments.""" + import getopt + cfg.program_name = sys.argv[0] or 'pynslcd' + try: + optlist, args = getopt.gnu_getopt(sys.argv[1:], + 'cdhV', ('check', 'debug', 'help', 'version', )) + for flag, arg in optlist: + if flag in ('-c', '--check'): + cfg.check = True + elif flag in ('-d', '--debug'): + cfg.debug += 1 + elif flag in ('-h', '--help'): + display_usage(sys.stdout) + sys.exit(0) + elif flag in ('-V', '--version'): + display_version(sys.stdout) + sys.exit(0) + if len(args): + raise getopt.GetoptError('unrecognized option \'%s\'' % args[0], args[0]) + except getopt.GetoptError, reason: + sys.stderr.write("%(program_name)s: %(reason)s\n" + "Try '%(program_name)s --help' for more information.\n" + % { 'program_name': cfg.program_name, + 'reason': reason, }) + sys.exit(1) + +def create_socket(): + """Returns a socket ready to answer requests from the client.""" + import socket + import fcntl + sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + # remove existing named socket + try: + os.unlink(config.NSLCD_SOCKET) + except OSError: + pass # ignore any problems + # bind to named socket + sock.bind((config.NSLCD_SOCKET)) + # close the file descriptor on exit + fcntl.fcntl(sock, fcntl.F_SETFD, fcntl.FD_CLOEXEC) + # set permissions of socket so anybody can do requests + os.chmod(config.NSLCD_SOCKET, 0666) + # start listening for connections + sock.listen(socket.SOMAXCONN) + return sock + +def log_newsession(): + pass + # FIXME: implement + +def getpeercred(fd): + return (None, None, None) + # FIXME: implement and return uid, gid, pid + +handlers = {} +handlers.update(common.get_handlers('alias')) +handlers.update(common.get_handlers('ether')) +handlers.update(common.get_handlers('group')) +handlers.update(common.get_handlers('pam')) +handlers.update(common.get_handlers('passwd')) +handlers.update(common.get_handlers('shadow')) + +def acceptconnection(session): + # accept a new connection + conn, addr = nslcd_serversocket.accept() + # See: http://docs.python.org/library/socket.html#socket.socket.settimeout + fp = None + try: + # probably use finally + # indicate new connection to logging module (genrates unique id) + log_newsession() + # log connection + try: + uid, gid, pid = getpeercred(conn) + logging.debug('connection from pid=%r uid=%r gid=%r', pid, uid, gid) + except: + raise # FIXME: handle exception gracefully + # create a stream object + fp = TIOStream(conn) + # read request + version = fp.read_int32() + if version != constants.NSLCD_VERSION: + logging.debug('wrong nslcd version id (%r)', version) + return + action = fp.read_int32() + try: + handler = handlers[action] + except KeyError: + logging.warn('invalid action id: %r', action) + return + handler(fp, session, uid)() + finally: + if fp: + fp.close() + +def disable_nss_ldap(): + """Disable the nss_ldap module to avoid lookup loops.""" + import ctypes + lib = ctypes.CDLL(config.NSS_LDAP_SONAME) + ctypes.c_int.in_dll(lib, '_nss_ldap_enablelookups').value = 0 + +def worker(): + # create a new LDAP session + #session = myldap_create_session() + session = ldap.initialize(cfg.ldap_uri) + # start waiting for incoming connections + while True: + # wait for a new connection + acceptconnection(session) + # FIXME: handle exceptions + +if __name__ == '__main__': + # parse options + parse_cmdline() + # clean the environment + os.environ.clear() + os.putenv('HOME', '/') + os.putenv('TMPDIR', '/tmp') + os.putenv('LDAPNOINIT', '1') + # disable ldap lookups of host names to avoid lookup loop + disable_nss_ldap() + # set log level + if cfg.debug: + logging.getLogger().setLevel(logging.DEBUG) + # FIXME: implement + #if myldap_set_debuglevel(cfg.debug) != LDAP_SUCCESS: + # sys.exit(1) + # read configuration file + cfg.read(config.NSLCD_CONF_PATH) + # set a default umask for the pidfile and socket + os.umask(0022) + # see if someone already locked the pidfile + pidfile = mypidfile.MyPIDLockFile(config.NSLCD_PIDFILE) + # see if --check option was given + if cfg.check: + if pidfile.is_locked(): + logging.debug('pidfile (%s) is locked', config.NSLCD_PIDFILE) + sys.exit(0) + else: + logging.debug('pidfile (%s) is not locked', config.NSLCD_PIDFILE) + sys.exit(1) + # normal check for pidfile locked + if pidfile.is_locked(): + logging.error('daemon may already be active, cannot acquire lock (%s)', config.NSLCD_PIDFILE) + sys.exit(1) + # daemonize + if cfg.debug: + daemon = pidfile + else: + daemon = daemon.DaemonContext( + pidfile=pidfile, + signal_map={ + signal.SIGTERM: 'terminate', + signal.SIGINT: 'terminate', + signal.SIGPIPE: None, + }) + # start daemon + with daemon: + # start normal logging + if not cfg.debug: + log_startlogging(); + logging.info('version %s starting', config.VERSION) + # create socket + nslcd_serversocket = create_socket(); + # drop all supplemental groups + try: + os.setgroups(()) + except OSError, e: + logging.warn('cannot setgroups(()) (ignored): %s', e) + # change to nslcd gid + if cfg.gid is not None: + import grp + os.setgid(grp.getgrnam(cfg.gid).gr_gid) + # change to nslcd uid + if cfg.uid is not None: + import pwd + u = pwd.getpwnam(cfg.uid) + os.setuid(u.pw_uid) + os.environ['HOME'] = u.pw_dir + logging.info('accepting connections') + # start worker threads + threads = [] + for i in range(cfg.threads): + thread = threading.Thread(target=worker, name='thread%d' % i) + thread.setDaemon(True) + thread.start() + logging.debug('started thread %s' % thread.getName()) + threads.append(thread) + # wait for all threads to die + for thread in threads: + thread.join(10000) diff --git a/pynslcd/shadow.py b/pynslcd/shadow.py new file mode 100644 index 0000000..3f2a5d7 --- /dev/null +++ b/pynslcd/shadow.py @@ -0,0 +1,116 @@ + +# shadow.py - lookup functions for shadownet addresses +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import constants +import common + +import ldap.filter + + +class ShadowRequest(common.Request): + + filter = '(objectClass=shadowAccount)' + + attmap_uid = 'uid' + attmap_userPassword = 'userPassword' + attmap_shadowLastChange = 'shadowLastChange' + attmap_shadowMin = 'shadowMin' + attmap_shadowMax = 'shadowMax' + attmap_shadowWarning = 'shadowWarning' + attmap_shadowInactive = 'shadowInactive' + attmap_shadowExpire = 'shadowExpire' + attmap_shadowFlag = 'shadowFlag' + + attributes = ( 'uid', 'userPassword', 'shadowLastChange', 'shadowMin', + 'shadowMax', 'shadowWarning', 'shadowInactive', + 'shadowExpire', 'shadowFlag' ) + + bases = ( 'ou=people,dc=test,dc=tld', ) + + def write(self, entry): + dn, attributes = entry + # get name and check against requested name + names = attributes.get(self.attmap_uid, []) + if not names: + print 'Error: entry %s does not contain %s value' % ( dn, self.attmap_uid) + return + if self.name: + if self.name not in names: + return + names = ( self.name, ) + # get password + (passwd, ) = attributes.get(self.attmap_userPassword, ['x']) + if not passwd or self.calleruid != 0: + passwd = '*'; + # function for making an int + def mk_int(attr): + try: + return + except TypeError: + return None + # get lastchange date + lastchangedate = int(attributes.get(self.attmap_shadowLastChange, [-1])[0]) + # we expect an AD 64-bit datetime value; + # we should do date=date/864000000000-134774 + # but that causes problems on 32-bit platforms, + # first we devide by 1000000000 by stripping the + # last 9 digits from the string and going from there */ + if self.attmap_shadowLastChange == 'pwdLastSet': + lastchangedate = ( lastchangedate / 864000000000 ) - 134774 + # get longs + mindays = int(attributes.get(self.attmap_shadowMin, [-1])[0]) + maxdays = int(attributes.get(self.attmap_shadowMax, [-1])[0]) + warndays = int(attributes.get(self.attmap_shadowWarning, [-1])[0]) + inactdays = int(attributes.get(self.attmap_shadowInactive, [-1])[0]) + expiredate = int(attributes.get(self.attmap_shadowExpire, [-1])[0]) + flag = int(attributes.get(self.attmap_shadowFlag, [0])[0]) + if self.attmap_shadowFlag == 'pwdLastSet': + if flag & 0x10000: + maxdays = 99999 + flag = 0 + # write results + for name in names: + self.fp.write_int32(constants.NSLCD_RESULT_BEGIN) + self.fp.write_string(name) + self.fp.write_string(passwd) + self.fp.write_int32(lastchangedate) + self.fp.write_int32(mindays) + self.fp.write_int32(maxdays) + self.fp.write_int32(warndays) + self.fp.write_int32(inactdays) + self.fp.write_int32(expiredate) + self.fp.write_int32(flag) + + +class ShadowByNameRequest(ShadowRequest): + + action = constants.NSLCD_ACTION_SHADOW_BYNAME + + def read_parameters(self): + self.name = self.fp.read_string() + + def mk_filter(self): + return '(&%s(%s=%s))' % ( self.filter, + self.attmap_uid, ldap.filter.escape_filter_chars(self.name) ) + + +class ShadowAllRequest(ShadowRequest): + + action = constants.NSLCD_ACTION_SHADOW_ALL diff --git a/pynslcd/tio.py b/pynslcd/tio.py new file mode 100644 index 0000000..acba81f --- /dev/null +++ b/pynslcd/tio.py @@ -0,0 +1,98 @@ + +# tio.py - I/O functions +# +# Copyright (C) 2010 Arthur de Jong +# +# This library is free software; you can redistribute it and/or +# modify it under the terms of the GNU Lesser General Public +# License as published by the Free Software Foundation; either +# version 2.1 of the License, or (at your option) any later version. +# +# This library 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 +# Lesser General Public License for more details. +# +# You should have received a copy of the GNU Lesser General Public +# License along with this library; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA +# 02110-1301 USA + +import struct +import os +import socket +import errno + +# definition for reading and writing INT32 values +_int32 = struct.Struct('i') + +# FIXME: use something from config.py to determine the correct size +_uid_t = struct.Struct('i') + +# FIXME: use something from config.py to determine the correct size +_gid_t = struct.Struct('i') + +# FIXME: use something from config.py to determine the correct size +_struct_timeval = struct.Struct('ll') + +class TIOStreamError(Exception): + pass + +class TIOStream(object): + """File-like object that allows reading and writing nslcd-protocol + entities.""" + + def __init__(self, conn): + conn.setblocking(1) + conn.setsockopt(socket.SOL_SOCKET, socket.SO_RCVTIMEO, _struct_timeval.pack(0, 500000)) + conn.setsockopt(socket.SOL_SOCKET, socket.SO_SNDTIMEO, _struct_timeval.pack(60, 0)) + self.fp = os.fdopen(conn.fileno(), 'w+b', 1024*1024) + + def read(self, size): + return self.fp.read(size) + + def read_int32(self): + return _int32.unpack(self.read(_int32.size))[0] + + def read_uid_t(self): + return _uid_t.unpack(self.read(_uid_t.size))[0] + + def read_gid_t(self): + return _gid_t.unpack(self.read(_gid_t.size))[0] + + def read_string(self, maxsize=None): + len = self.read_int32() + if maxsize and len >= maxsize: + raise TIOStreamError() + return self.read(len) + + def write(self, value): + self.fp.write(value) + + def write_int32(self, value): + self.write(_int32.pack(value)) + + def write_uid_t(self, value): + self.write(_uid_t.pack(value)) + + def write_gid_t(self, value): + self.write(_gid_t.pack(value)) + + def write_string(self, value): + self.write_int32(len(value)) + self.write(value) + + def write_stringlist(self, value): + lst = tuple(value) + self.write_int32(len(lst)) + for string in lst: + self.write_string(string) + + def close(self): + try: + self.fp.close() + except IOError: + pass + + def __del__(self): + self.close() diff --git a/tests/README b/tests/README index 61b30f8..83c11f4 100644 --- a/tests/README +++ b/tests/README @@ -70,6 +70,9 @@ bind_timelimit 4 reconnect_sleeptime 4 reconnect_retrytime 10 filter group (|(objectClass=posixGroup)(objectClass=groupOfUniqueNames)) +base passwd ou=people,dc=test,dc=tld +base shadow ou=people,dc=test,dc=tld +base group ou=groups,dc=test,dc=tld OLD TESTS diff --git a/tests/nslcd-test.conf b/tests/nslcd-test.conf index e55d194..d3430da 100644 --- a/tests/nslcd-test.conf +++ b/tests/nslcd-test.conf @@ -19,7 +19,7 @@ base dc=test,dc=tld #scope sub # The number of answers to request in a single search. -pagesize 100 +#pagesize 100 # The timeout for network operations. timelimit 2 diff --git a/tests/test.ldif.gz b/tests/test.ldif.gz Binary files differindex 0c2c2d1..bea4bb9 100644 --- a/tests/test.ldif.gz +++ b/tests/test.ldif.gz diff --git a/tests/test_nsscmds.sh b/tests/test_nsscmds.sh index d435866..a930699 100755 --- a/tests/test_nsscmds.sh +++ b/tests/test_nsscmds.sh @@ -153,7 +153,17 @@ EOM echo "test_nsscmds.sh: testing group..." -check "getent group testgroup" << EOM +# function to sort group members of a group +sortgroup() { + while read line + do + group="$(echo "$line" | sed 's/^\(.*:.*:.*:\).*/\1/')" + members="$(echo "$line" | sed 's/^.*:.*:.*://' | tr ',' '\n' | sort | tr '\n' ',' | sed 's/,$//')" + echo "${group}${members}" + done +} + +check "getent group testgroup | sortgroup" << EOM testgroup:*:6100:arthur,test,testuser4 EOM @@ -167,7 +177,7 @@ EOM check "getent group TESTGROUP" << EOM EOM -check "getent group 6100" << EOM +check "getent group 6100 | sortgroup" << EOM testgroup:*:6100:arthur,test,testuser4 EOM @@ -179,7 +189,7 @@ check "groups testuser4 | sed 's/^.*://'" << EOM users testgroup testgroup2 EOM -check "getent group | egrep '^(testgroup|users):'" << EOM +check "getent group | egrep '^(testgroup|users):' | sortgroup" << EOM users:x:100: testgroup:*:6100:arthur,test,testuser4 users:*:100:arthur,test @@ -189,20 +199,20 @@ check "getent group | wc -l" << EOM `grep -c : /etc/group | awk '{print $1 + 5}'` EOM -check "getent group | grep ^largegroup" << EOM -largegroup:*:1005:oebrani,hpaek,enastasi,sgurski,hsweezer,utrezize,ihashbarger,lkhubba,rlatessa,behrke,kbradbury,hmachesky,hhydrick,dciviello,wselim,ngata,gcubbison,testusr2,hgalavis,hhaffey,testusr3,yautin,wvalcin,jyeater,slaforge,vpender,lvittum,hpolk,rkoonz,ngullett,btempel,igurwell,rworkowski,phaye,lbuchtel,nfunchess,fcunard,cmanno,nfilipek,dfirpo,vdelnegro,hzagami,htomlinson,khathway,gzuhlke,wworf,tabdelal,mjuris,okveton,dbye,wbrettschneide,kklavetter,ndipanfilo,psowa,osaines,uschweyen,vwaltmann,nkraker,dgivliani,purquilla,otrevor,ghanauer,oclunes,gdreitzler,gdaub,nroepke,mciaccia,tpaa,gtinnel,tfalconeri,cjody,vmigliori,vleyton,alat,znightingale,showe,zwinterbottom,lgandee,vmedici,lseehafer,gpomerance,mbodley,bdevera,bmoldan,akraskouskas,pdossous,sdebry,gsusoev,gvollrath,nriofrio,mblanchet,lmauracher,dgosser,ameisinger,clouder,ykisak,emcquiddy,zgingrich,vchevalier,nrybij +check "getent group | grep ^largegroup | sortgroup" << EOM +largegroup:*:1005:akraskouskas,alat,ameisinger,bdevera,behrke,bmoldan,btempel,cjody,clouder,cmanno,dbye,dciviello,dfirpo,dgivliani,dgosser,emcquiddy,enastasi,fcunard,gcubbison,gdaub,gdreitzler,ghanauer,gpomerance,gsusoev,gtinnel,gvollrath,gzuhlke,hgalavis,hhaffey,hhydrick,hmachesky,hpaek,hpolk,hsweezer,htomlinson,hzagami,igurwell,ihashbarger,jyeater,kbradbury,khathway,kklavetter,lbuchtel,lgandee,lkhubba,lmauracher,lseehafer,lvittum,mblanchet,mbodley,mciaccia,mjuris,ndipanfilo,nfilipek,nfunchess,ngata,ngullett,nkraker,nriofrio,nroepke,nrybij,oclunes,oebrani,okveton,osaines,otrevor,pdossous,phaye,psowa,purquilla,rkoonz,rlatessa,rworkowski,sdebry,sgurski,showe,slaforge,tabdelal,testusr2,testusr3,tfalconeri,tpaa,uschweyen,utrezize,vchevalier,vdelnegro,vleyton,vmedici,vmigliori,vpender,vwaltmann,wbrettschneide,wselim,wvalcin,wworf,yautin,ykisak,zgingrich,znightingale,zwinterbottom EOM -check "getent group largegroup" << EOM -largegroup:*:1005:oebrani,hpaek,enastasi,sgurski,hsweezer,utrezize,ihashbarger,lkhubba,rlatessa,behrke,kbradbury,hmachesky,hhydrick,dciviello,wselim,ngata,gcubbison,testusr2,hgalavis,hhaffey,testusr3,yautin,wvalcin,jyeater,slaforge,vpender,lvittum,hpolk,rkoonz,ngullett,btempel,igurwell,rworkowski,phaye,lbuchtel,nfunchess,fcunard,cmanno,nfilipek,dfirpo,vdelnegro,hzagami,htomlinson,khathway,gzuhlke,wworf,tabdelal,mjuris,okveton,dbye,wbrettschneide,kklavetter,ndipanfilo,psowa,osaines,uschweyen,vwaltmann,nkraker,dgivliani,purquilla,otrevor,ghanauer,oclunes,gdreitzler,gdaub,nroepke,mciaccia,tpaa,gtinnel,tfalconeri,cjody,vmigliori,vleyton,alat,znightingale,showe,zwinterbottom,lgandee,vmedici,lseehafer,gpomerance,mbodley,bdevera,bmoldan,akraskouskas,pdossous,sdebry,gsusoev,gvollrath,nriofrio,mblanchet,lmauracher,dgosser,ameisinger,clouder,ykisak,emcquiddy,zgingrich,vchevalier,nrybij +check "getent group largegroup | sortgroup" << EOM +largegroup:*:1005:akraskouskas,alat,ameisinger,bdevera,behrke,bmoldan,btempel,cjody,clouder,cmanno,dbye,dciviello,dfirpo,dgivliani,dgosser,emcquiddy,enastasi,fcunard,gcubbison,gdaub,gdreitzler,ghanauer,gpomerance,gsusoev,gtinnel,gvollrath,gzuhlke,hgalavis,hhaffey,hhydrick,hmachesky,hpaek,hpolk,hsweezer,htomlinson,hzagami,igurwell,ihashbarger,jyeater,kbradbury,khathway,kklavetter,lbuchtel,lgandee,lkhubba,lmauracher,lseehafer,lvittum,mblanchet,mbodley,mciaccia,mjuris,ndipanfilo,nfilipek,nfunchess,ngata,ngullett,nkraker,nriofrio,nroepke,nrybij,oclunes,oebrani,okveton,osaines,otrevor,pdossous,phaye,psowa,purquilla,rkoonz,rlatessa,rworkowski,sdebry,sgurski,showe,slaforge,tabdelal,testusr2,testusr3,tfalconeri,tpaa,uschweyen,utrezize,vchevalier,vdelnegro,vleyton,vmedici,vmigliori,vpender,vwaltmann,wbrettschneide,wselim,wvalcin,wworf,yautin,ykisak,zgingrich,znightingale,zwinterbottom EOM -check "getent group | grep ^hugegroup" << EOM -hugegroup:*:1006:amccroskey,erathert,rrasual,mlinak,psiroky,ichewning,dtuholski,yautin,denriquez,yolivier,tnitzel,kmuros,ppedraja,mrizer,jsweezy,nriofrio,joligee,klitehiser,emcquiddy,gallanson,dbertels,tcossa,hhagee,blovig,ebattee,khartness,nforti,kfend,sgunder,wesguerra,yduft,jzych,edrinkwater,esonia,pphuaphes,ualway,tmysinger,tnaillon,ygockel,sbettridge,clapenta,igizzi,svogler,pbrentano,emanikowski,uwalpole,kwinterling,ghumbles,lparrish,ewilles,oebrani,gdrilling,wtruman,ggillim,phyer,hholyfield,epoinelli,nagerton,wbrill,bswantak,bdadds,vstirman,hbukovsky,lgadomski,sskyers,ddeguire,ekalfas,tbagne,yeven,rdubs,wvalcin,mdoering,rfidel,hkippes,lmichaud,vburton,charriman,hkarney,mswogger,klundsten,nciucci,rpastorin,tcacal,rramirez,thelfritz,hschoepfer,sdebry,vbaldasaro,asivley,vpender,akravetz,llarmore,vmaynard,lmcgeary,rheinzmann,kthede,gcummer,opoch,akertzman,ngrowney,lsobrino,hveader,jspohn,cabare,hrenart,sbrabyn,ohatto,hbrandow,dhammontree,kwidrick,ascovel,jskafec,uslavinski,imcbay,wclokecloak,cflenner,hbastidos,lcaudell,gcarlini,opuglisi,nbugtong,hbetterman,lshilling,nfunchess,nlainhart,kconkey,ktuccio,mcontreras,dasiedu,cbotdorf,rchevrette,mgavet,hchaviano,zwinterbottom,fthein,zculp,bdominga,dlargo,hbickford,lrandall,ykimbel,lautovino,cfasone,hdoiel,ediga,hmatonak,fmilsaps,amckinney,mquigg,mvanpelt,daubert,dgiacomazzi,hhysong,svielle,zanderlik,mpizzaro,bromano,kmarzili,uweyand,smullowney,rbernhagen,ajaquess,ekeuper,lbove,greiff,uransford,ewicks,cpentreath,kepps,uhayakawa,tmccamish,rdubuisson,dtashjian,ibreitbart,ffigert,ycostaneda,kmedcaf,fgrashot,tredfearn,nedgin,mrydelek,tsowells,ilamberth,hhartranft,dsharr,oport,areid,bbeckfield,bluellen,fagro,ihegener,sackles,fparness,lvaleriano,faleo,fbielecki,jeuresti,lcavez,nerbach,tschnepel,zkutchera,limbrogno,nkubley,afredin,gwaud,bmoling,rschkade,kfaure,vtresch,ekurter,estockwin,rgoonez,erostad,nrysavy,hhaffey,apurdon,llasher,jholzmiller,ashuey,rbillingsly,osaber,asemons,edurick,tgindhart,svongal,mvedder,jvillaire,dholdaway,bbrenton,lpitek,jjumalon,kjoslyn,cparee,cklem,mcoch,kmcardle,dsteever,nlemma,rfauerbach,wnunziata,fsirianni,dciullo,udatu,cjody,mvas,hkinderknecht,cpencil,rmcstay,tboxx,brodgerson,mfeil,eberkman,gdeblasio,hspiry,ilevian,wdagrella,bharnois,sscheiern,vbigalow,nschmig,pwohlenhaus,uflander,ckodish,amcgraw,cswigert,mcampagnone,inarain,kmcguire,tharr,bdaughenbaugh,garchambeault,bmarszalek,pvirelli,snotari,nspolar,skanjirathinga,fsunderland,mmesidor,lmuehlberger,glafontaine,aferge,hcarrizal,pdurando,gdeyarmond,fmarchi,wstjean,obeaufait,nslaby,dlongbotham,tplatko,jcaroll,isplonskowski,zscammahorn,sstuemke,cnoriego,nsiemonsma,lseabold,cmafnas,dhendon,bfishbeck,gkerens,eklunder,fburrough,ebusk,tmarkus,clouder,cweiss,mpellew,ojerabek,veisenhardt,vwokwicz,tvrooman,rpitter,slerew,dwittlinger,habby,mpanahon,rguinane,zneeb,eyounglas,gcervantez,kbrugal,ycobetto,tkeala,pheathcock,cmellberg,hmiazga,bmicklos,bphou,ngullett,jwinterton,lcremer,jmartha,icoard,ahandy,eparham,gtinnel,wganther,umarbury,fhalon,bsibal,uschweyen,gearnshaw,cbleimehl,omasone,cdeckard,ctetteh,arosel,pmineo,gclapham,jamber,sbonnie,eaguire,jmarugg,ihalford,wdovey,sarndt,gbitar,ovibbert,ewismer,gmilian,rginer,gdaub,showe,hlynema,rtooker,svandewalle,fhain,jlunney,jreigh,kmandolfo,leberhardt,wkhazaleh,nasmar,egrago,ablackstock,lcocherell,pvierthaler,vrunyon,kpalka,ubenken,hmuscaro,jherkenratt,pminnis,bscadden,srubenfield,cnabzdyk,mpytko,gchounlapane,pwademan,nousdahl,pcornn,zmeeker,hpalmquist,jrees,mkofoed,mkibler,lbassin,fplayfair,hmogush,nvyhnal,ileaman,gschaumburg,thoch,wconces,hliverman,gmackinder,rbrisby,isowder,rkraszewski,hzagami,obihl,nhelfinstine,mbravata,thynson,vwaltmann,tlana,ggehrke,pwutzke,zbuscaglia,ewuitschick,hgalavis,ddigerolamo,wmendell,etunby,jkimpton,mheilbrun,laksamit,hvannette,jseen,sgurski,iroiger,lcanestrini,baigner,dminozzi,uazatyan,gjankowiak,bstrede,mstirn,hfludd,mdyce,tbattista,gfaire,gapkin,esproull,gcurnutt,tstalworth,ienglert,hbrehmer,csoomaroo,kaanerud,nlinarez,jeverton,uspittler,prowena,gsantella,oreiss,rcheshier,tpaa,kwirght,gparkersmith,jquicksall,xrahaim,vwisinger,aesbensen,eorsten,imensah,omalvaez,dnegri,wmailey,tyounglas,vtowell,pgrybel,lmauracher,lschollmeier,ithum,umosser,pbeckerdite,hsabol,dhindsman,ugerpheide,gconver,lhuggler,amanganelli,omatula,zhaulk,lkimel,mruppel,egospatrick,kseisler,ehindbaugh,mdecourcey,kbartolet,vcrofton,cdegravelle,ksiering,fvallian,kalguire,dblazejewski,vdesir,tairth,hcusta,mjeon,smccaie,hpolintan,ihimmelwright,fbeatrice,yvdberg,uednilao,vmedici,sskone,dbarriball,ndrumgole,ccyganiewicz,cdrumm,usevera,vsefcovic,mfitzherbert,fberyman,upater,vpiraino,pwashuk,kshippy,bcolorado,cbarlup,cmiramon,kdevincent,mcaram,cbourek,hkohlmeyer,lringuette,lgradilla,slaningham,ksparling,tcrissinger,senrico,dlanois,iyorks,gbolay,rpikes,hcafourek,shaith,fverfaille,btheim,iambrosino,ghann,fkeef,tsearle,tsepulueda,iherrarte,fvinal,sherzberg,iiffert,astrunk,ghelderman,moller,gmassi,oahyou,cjuntunen,mvanbergen,tkelly,eziebert,nhija,sjankauskas,pdech,mmangiamele,clewicki,meconomides,tmccaffity,carguellez,prepasky,amaslyn,kmallach,ejeppesen,hwoodert,dgivliani,nglathar,fwidhalm,kheadlon,ihernan,oshough,nevan,mpilon,mviverette,beon,alat,ktriblett,ivanschaack,vnazzal,lwedner,alienhard,slaudeman,cpalmios,gishii,kpuebla,ascheno,ocrabbs,dledenbach,ebeachem,ideveyra,sspagnuolo,fsymmonds,srees,isteinlicht,bveeneman,myokoyama,agordner,xlantey,broher,bpinedo,psharits,iweibe,nchrisman,htomlinson,cdickes,draymundo,jbielicki,ulanigan,ihanneman,ppeper,ljomes,khovanesian,ibeto,ilacourse,iseipel,iogasawara,jglotzbecker,mferandez,gpomerance,pdulac,mgayden,skoegler,kbattershell,uvanmatre,wvermeulen,ekenady,ikulbida,htsuha,lvanconant,njordon,oosterhouse,tmelland,lspielvogel,bmarlin,bouten,fgoben,bjolly,iyorgey,htilzer,dgosser,gcobane,vpeairs,dloubier,zfarler,fvascones,awhitt,cscullion,nkempon,rgriffies,wconstantino,opizzuti,scocuzza,pgreenier,ueriks,cwank,mdanos,kmisove,ndesautels,hlichota,cgalinol,rlambertus,zvagt,ohoffert,vchevalier,vwabasha,amayorga,mtintle,rbloomstrand,swoodie,gportolese,hriech,ckerska,gvollrath,bdevera,lmadruga,mbeagley,hdyner,fcha,rlatessa,lsivic,mdedon,mcashett,ubynum,lcoulon,cbrechbill,kgremminger,yfrymoyer,pahles,guresti,kmayoras,mbodley,phalkett,kolexa,fsapien,cghianni,oalthouse,mpark,mlenning,gfedewa,imicthell,farquette,nhayer,vglidden,tkhora,mneubacher,esthill,ecolden,nnamanworth,eklein,pgiegerich,smillian,nmccolm,ameisinger,rtole,jsegundo,jknight,behrke,tguinnip,wlynch,tmorr,omcdaid,dfollman,kmosko,mground,pfavolise,dfirpo,aponcedeleon,wenglander,pduitscher,emehta,lyoula,bmadamba,critchie,gloebs,jscheitlin,tsann,tmalecki,okave,dsherard,wdevenish,dmahapatra,redling,venfort,hstreitnatter,tfetherston,jsenavanh,mmerriwether,pbondroff,tabdelal,badair,bhelverson,jlebouf,tfalconeri,sgefroh,mredd,wselim,ikadar,nrybij,eathey,pschrayter,gmings,xeppley,hrapisura,tdonathan,bcoletta,mdickinson,vdolan,pbiggart,ibyles,kcomparoni,jmatty,psundeen,imarungo,cmcanulty,tmcmickle,obenallack,qhanly,saben,owhitelow,dtornow,btempel,agimm,cpluid,ktoni,rlosinger,fnottage,mfaeth,tmurata,fcunard,saycock,mmcchristian,mcasida,kmoesch,kottomaniello,bwynes,emargulis,kbarnthouse,psalesky,mlinardi,fberra,cgaudette,sestergard,afuchs,esheehan,dscheurer,sgropper,jbjorkman,dflore,vbonder,nnickel,klurie,hmateer,lseehafer,cpinela,maustine,zratti,ohove,okveton,mhollings,vrodick,nwescott,mtanzi,ktuner,yschmuff,akraskouskas,lschnorbus,dmcgillen,aziernicki,wleiva,nendicott,kcofrancesco,cmanno,deshmon,adenicola,hlauchaire,mlaverde,kpenale,dmarchizano,pviviani,vemily,agarbett,ohedlund,werrick,imillin,oconerly,wottesen,kmeester,nwiker,nranck,jroman,cspilis,mallmand,yhenriques,nphan,nbuford,nlohmiller,istallcup,hzinda,atollefsrud,spolmer,purquilla,bgavagan,nramones,lnormand,adishaw,jdodge,moser,urosentrance,oclunes,lpeagler,ubieniek,sgirsh,dzurek,hlemon,pwetherwax,wcreggett,kgarced,pthornberry,nmoren,gcukaj,lbuchtel,dcaltabiano,ibuzo,akomsthoeft,upellam,ptraweek,abortignon,ralspach,pcaposole,hcintron,cbartnick,vnery,lfarraj,pwhitmire,kpannunzio,vfeigel,lpintor,tlowers,fsplinter,rfassinger,ofelcher,csever,oolivarez,kbrevitz,ctuzzo,owhelchel,ptoenjes,mskeele,lschenkelberg,tsablea,hloftis,cbelardo,ycerasoli,gmoen,obercier,cfleurantin,hbraim,ihoa,ochasten,fsavela,zborgmeyer,sbemo,mcolehour,vtrumpp,lgandee,atonkin,rpinilla,hsweezer,hwestermark,lbanco,bwinterton,hcowles,ninnella,ehathcock,uholecek,alamour,bguthary,mdimaio,lsous,ecelestin,ademosthenes,ncermeno,vkrug,ngiesler,pdauterman,achhor,hpimpare,epeterson,lfichtner,tgelen,pdischinger,nlatchaw,psabado,ecordas,dpebbles,ckistenmacher,oscarpello,hschelb,nridinger,tvehrs,lpondexter,rgramby,ocalleo,imuehl,istarring,teliades,ctenny,kstachurski,ugreenberg,cpaccione,cgaler,mmattu,opeet,sstough,dlablue,mespinel,sbloise,ohearl,cbrom,krahman,ysnock,vlubic,rmandril,eserrett,gshrode,ksollitto,ilawbaugh,jappleyard,pbascom,rnordby +check "getent group | grep ^hugegroup | sortgroup" << EOM +hugegroup:*:1006:ablackstock,abortignon,achhor,ademosthenes,adenicola,adishaw,aesbensen,aferge,afredin,afuchs,agarbett,agimm,agordner,ahandy,ajaquess,akertzman,akomsthoeft,akraskouskas,akravetz,alamour,alat,alienhard,amanganelli,amaslyn,amayorga,amccroskey,amcgraw,amckinney,ameisinger,aponcedeleon,apurdon,areid,arosel,ascheno,ascovel,asemons,ashuey,asivley,astrunk,atollefsrud,atonkin,awhitt,aziernicki,badair,baigner,bbeckfield,bbrenton,bcoletta,bcolorado,bdadds,bdaughenbaugh,bdevera,bdominga,behrke,beon,bfishbeck,bgavagan,bguthary,bharnois,bhelverson,bjolly,blovig,bluellen,bmadamba,bmarlin,bmarszalek,bmicklos,bmoling,bouten,bphou,bpinedo,brodgerson,broher,bromano,bscadden,bsibal,bstrede,bswantak,btempel,btheim,bveeneman,bwinterton,bwynes,cabare,carguellez,cbarlup,cbartnick,cbelardo,cbleimehl,cbotdorf,cbourek,cbrechbill,cbrom,ccyganiewicz,cdeckard,cdegravelle,cdickes,cdrumm,cfasone,cflenner,cfleurantin,cgaler,cgalinol,cgaudette,cghianni,charriman,cjody,cjuntunen,ckerska,ckistenmacher,cklem,ckodish,clapenta,clewicki,clouder,cmafnas,cmanno,cmcanulty,cmellberg,cmiramon,cnabzdyk,cnoriego,cpaccione,cpalmios,cparee,cpencil,cpentreath,cpinela,cpluid,critchie,cscullion,csever,csoomaroo,cspilis,cswigert,ctenny,ctetteh,ctuzzo,cwank,cweiss,dasiedu,daubert,dbarriball,dbertels,dblazejewski,dcaltabiano,dciullo,ddeguire,ddigerolamo,denriquez,deshmon,dfirpo,dflore,dfollman,dgiacomazzi,dgivliani,dgosser,dhammontree,dhendon,dhindsman,dholdaway,dlablue,dlanois,dlargo,dledenbach,dlongbotham,dloubier,dmahapatra,dmarchizano,dmcgillen,dminozzi,dnegri,dpebbles,draymundo,dscheurer,dsharr,dsherard,dsteever,dtashjian,dtornow,dtuholski,dwittlinger,dzurek,eaguire,eathey,ebattee,ebeachem,eberkman,ebusk,ecelestin,ecolden,ecordas,ediga,edrinkwater,edurick,egospatrick,egrago,ehathcock,ehindbaugh,ejeppesen,ekalfas,ekenady,ekeuper,eklein,eklunder,ekurter,emanikowski,emargulis,emcquiddy,emehta,eorsten,eparham,epeterson,epoinelli,erathert,erostad,eserrett,esheehan,esonia,esproull,esthill,estockwin,etunby,ewicks,ewilles,ewismer,ewuitschick,eyounglas,eziebert,fagro,faleo,farquette,fbeatrice,fberra,fberyman,fbielecki,fburrough,fcha,fcunard,ffigert,fgoben,fgrashot,fhain,fhalon,fkeef,fmarchi,fmilsaps,fnottage,fparness,fplayfair,fsapien,fsavela,fsirianni,fsplinter,fsunderland,fsymmonds,fthein,fvallian,fvascones,fverfaille,fvinal,fwidhalm,gallanson,gapkin,garchambeault,gbitar,gbolay,gcarlini,gcervantez,gchounlapane,gclapham,gcobane,gconver,gcukaj,gcummer,gcurnutt,gdaub,gdeblasio,gdeyarmond,gdrilling,gearnshaw,gfaire,gfedewa,ggehrke,ggillim,ghann,ghelderman,ghumbles,gishii,gjankowiak,gkerens,glafontaine,gloebs,gmackinder,gmassi,gmilian,gmings,gmoen,gparkersmith,gpomerance,gportolese,greiff,gsantella,gschaumburg,gshrode,gtinnel,guresti,gvollrath,gwaud,habby,hbastidos,hbetterman,hbickford,hbraim,hbrandow,hbrehmer,hbukovsky,hcafourek,hcarrizal,hchaviano,hcintron,hcowles,hcusta,hdoiel,hdyner,hfludd,hgalavis,hhaffey,hhagee,hhartranft,hholyfield,hhysong,hkarney,hkinderknecht,hkippes,hkohlmeyer,hlauchaire,hlemon,hlichota,hliverman,hloftis,hlynema,hmateer,hmatonak,hmiazga,hmogush,hmuscaro,hpalmquist,hpimpare,hpolintan,hrapisura,hrenart,hriech,hsabol,hschelb,hschoepfer,hspiry,hstreitnatter,hsweezer,htilzer,htomlinson,htsuha,hvannette,hveader,hwestermark,hwoodert,hzagami,hzinda,iambrosino,ibeto,ibreitbart,ibuzo,ibyles,ichewning,icoard,ideveyra,ienglert,igizzi,ihalford,ihanneman,ihegener,ihernan,iherrarte,ihimmelwright,ihoa,iiffert,ikadar,ikulbida,ilacourse,ilamberth,ilawbaugh,ileaman,ilevian,imarungo,imcbay,imensah,imicthell,imillin,imuehl,inarain,iogasawara,iroiger,iseipel,isowder,isplonskowski,istallcup,istarring,isteinlicht,ithum,ivanschaack,iweibe,iyorgey,iyorks,jamber,jappleyard,jbielicki,jbjorkman,jcaroll,jdodge,jeuresti,jeverton,jglotzbecker,jherkenratt,jholzmiller,jjumalon,jkimpton,jknight,jlebouf,jlunney,jmartha,jmarugg,jmatty,joligee,jquicksall,jrees,jreigh,jroman,jscheitlin,jseen,jsegundo,jsenavanh,jskafec,jspohn,jsweezy,jvillaire,jwinterton,jzych,kaanerud,kalguire,kbarnthouse,kbartolet,kbattershell,kbrevitz,kbrugal,kcofrancesco,kcomparoni,kconkey,kdevincent,kepps,kfaure,kfend,kgarced,kgremminger,khartness,kheadlon,khovanesian,kjoslyn,klitehiser,klundsten,klurie,kmallach,kmandolfo,kmarzili,kmayoras,kmcardle,kmcguire,kmedcaf,kmeester,kmisove,kmoesch,kmosko,kmuros,kolexa,kottomaniello,kpalka,kpannunzio,kpenale,kpuebla,krahman,kseisler,kshippy,ksiering,ksollitto,ksparling,kstachurski,kthede,ktoni,ktriblett,ktuccio,ktuner,kwidrick,kwinterling,kwirght,laksamit,lautovino,lbanco,lbassin,lbove,lbuchtel,lcanestrini,lcaudell,lcavez,lcocherell,lcoulon,lcremer,leberhardt,lfarraj,lfichtner,lgadomski,lgandee,lgradilla,lhuggler,limbrogno,ljomes,lkimel,llarmore,llasher,lmadruga,lmauracher,lmcgeary,lmichaud,lmuehlberger,lnormand,lparrish,lpeagler,lpintor,lpitek,lpondexter,lrandall,lringuette,lschenkelberg,lschnorbus,lschollmeier,lseabold,lseehafer,lshilling,lsivic,lsobrino,lsous,lspielvogel,lvaleriano,lvanconant,lwedner,lyoula,mallmand,maustine,mbeagley,mbodley,mbravata,mcampagnone,mcaram,mcashett,mcasida,mcoch,mcolehour,mcontreras,mdanos,mdecourcey,mdedon,mdickinson,mdimaio,mdoering,mdyce,meconomides,mespinel,mfaeth,mfeil,mferandez,mfitzherbert,mgavet,mgayden,mground,mheilbrun,mhollings,mjeon,mkibler,mkofoed,mlaverde,mlenning,mlinak,mlinardi,mmangiamele,mmattu,mmcchristian,mmerriwether,mmesidor,mneubacher,moller,moser,mpanahon,mpark,mpellew,mpilon,mpizzaro,mpytko,mquigg,mredd,mrizer,mruppel,mrydelek,mskeele,mstirn,mswogger,mtanzi,mtintle,mvanbergen,mvanpelt,mvas,mvedder,mviverette,myokoyama,nagerton,nasmar,nbuford,nbugtong,ncermeno,nchrisman,nciucci,ndesautels,ndrumgole,nedgin,nendicott,nerbach,nevan,nforti,nfunchess,ngiesler,nglathar,ngrowney,ngullett,nhayer,nhelfinstine,nhija,ninnella,njordon,nkempon,nkubley,nlainhart,nlatchaw,nlemma,nlinarez,nlohmiller,nmccolm,nmoren,nnamanworth,nnickel,nousdahl,nphan,nramones,nranck,nridinger,nriofrio,nrybij,nrysavy,nschmig,nsiemonsma,nslaby,nspolar,nvyhnal,nwescott,nwiker,oahyou,oalthouse,obeaufait,obenallack,obercier,obihl,ocalleo,ochasten,oclunes,oconerly,ocrabbs,oebrani,ofelcher,ohatto,ohearl,ohedlund,ohoffert,ohove,ojerabek,okave,okveton,omalvaez,omasone,omatula,omcdaid,oolivarez,oosterhouse,opeet,opizzuti,opoch,oport,opuglisi,oreiss,osaber,oscarpello,oshough,ovibbert,owhelchel,owhitelow,pahles,pbascom,pbeckerdite,pbiggart,pbondroff,pbrentano,pcaposole,pcornn,pdauterman,pdech,pdischinger,pduitscher,pdulac,pdurando,pfavolise,pgiegerich,pgreenier,pgrybel,phalkett,pheathcock,phyer,pmineo,pminnis,ppedraja,ppeper,pphuaphes,prepasky,prowena,psabado,psalesky,pschrayter,psharits,psiroky,psundeen,pthornberry,ptoenjes,ptraweek,purquilla,pvierthaler,pvirelli,pviviani,pwademan,pwashuk,pwetherwax,pwhitmire,pwohlenhaus,pwutzke,qhanly,ralspach,rbernhagen,rbillingsly,rbloomstrand,rbrisby,rcheshier,rchevrette,rdubs,rdubuisson,redling,rfassinger,rfauerbach,rfidel,rginer,rgoonez,rgramby,rgriffies,rguinane,rheinzmann,rkraszewski,rlambertus,rlatessa,rlosinger,rmandril,rmcstay,rnordby,rpastorin,rpikes,rpinilla,rpitter,rramirez,rrasual,rschkade,rtole,rtooker,saben,sackles,sarndt,saycock,sbemo,sbettridge,sbloise,sbonnie,sbrabyn,scocuzza,sdebry,senrico,sestergard,sgefroh,sgirsh,sgropper,sgunder,sgurski,shaith,sherzberg,showe,sjankauskas,skanjirathinga,skoegler,slaningham,slaudeman,slerew,smccaie,smillian,smullowney,snotari,spolmer,srees,srubenfield,sscheiern,sskone,sskyers,sspagnuolo,sstough,sstuemke,svandewalle,svielle,svogler,svongal,swoodie,tabdelal,tairth,tbagne,tbattista,tboxx,tcacal,tcossa,tcrissinger,tdonathan,teliades,tfalconeri,tfetherston,tgelen,tgindhart,tguinnip,tharr,thelfritz,thoch,thynson,tkeala,tkelly,tkhora,tlana,tlowers,tmalecki,tmarkus,tmccaffity,tmccamish,tmcmickle,tmelland,tmorr,tmurata,tmysinger,tnaillon,tnitzel,tpaa,tplatko,tredfearn,tsablea,tsann,tschnepel,tsearle,tsepulueda,tsowells,tstalworth,tvehrs,tvrooman,tyounglas,ualway,uazatyan,ubenken,ubieniek,ubynum,udatu,uednilao,ueriks,uflander,ugerpheide,ugreenberg,uhayakawa,uholecek,ulanigan,umarbury,umosser,upater,upellam,uransford,urosentrance,uschweyen,usevera,uslavinski,uspittler,uvanmatre,uwalpole,uweyand,vbaldasaro,vbigalow,vbonder,vburton,vchevalier,vcrofton,vdesir,vdolan,veisenhardt,vemily,venfort,vfeigel,vglidden,vkrug,vlubic,vmaynard,vmedici,vnazzal,vnery,vpeairs,vpender,vpiraino,vrodick,vrunyon,vsefcovic,vstirman,vtowell,vtresch,vtrumpp,vwabasha,vwaltmann,vwisinger,vwokwicz,wbrill,wclokecloak,wconces,wconstantino,wcreggett,wdagrella,wdevenish,wdovey,wenglander,werrick,wesguerra,wganther,wkhazaleh,wleiva,wlynch,wmailey,wmendell,wnunziata,wottesen,wselim,wstjean,wtruman,wvalcin,wvermeulen,xeppley,xlantey,xrahaim,yautin,ycerasoli,ycobetto,ycostaneda,yduft,yeven,yfrymoyer,ygockel,yhenriques,ykimbel,yolivier,yschmuff,ysnock,yvdberg,zanderlik,zborgmeyer,zbuscaglia,zculp,zfarler,zhaulk,zkutchera,zmeeker,zneeb,zratti,zscammahorn,zvagt,zwinterbottom EOM -check "getent group hugegroup" << EOM -hugegroup:*:1006:amccroskey,erathert,rrasual,mlinak,psiroky,ichewning,dtuholski,yautin,denriquez,yolivier,tnitzel,kmuros,ppedraja,mrizer,jsweezy,nriofrio,joligee,klitehiser,emcquiddy,gallanson,dbertels,tcossa,hhagee,blovig,ebattee,khartness,nforti,kfend,sgunder,wesguerra,yduft,jzych,edrinkwater,esonia,pphuaphes,ualway,tmysinger,tnaillon,ygockel,sbettridge,clapenta,igizzi,svogler,pbrentano,emanikowski,uwalpole,kwinterling,ghumbles,lparrish,ewilles,oebrani,gdrilling,wtruman,ggillim,phyer,hholyfield,epoinelli,nagerton,wbrill,bswantak,bdadds,vstirman,hbukovsky,lgadomski,sskyers,ddeguire,ekalfas,tbagne,yeven,rdubs,wvalcin,mdoering,rfidel,hkippes,lmichaud,vburton,charriman,hkarney,mswogger,klundsten,nciucci,rpastorin,tcacal,rramirez,thelfritz,hschoepfer,sdebry,vbaldasaro,asivley,vpender,akravetz,llarmore,vmaynard,lmcgeary,rheinzmann,kthede,gcummer,opoch,akertzman,ngrowney,lsobrino,hveader,jspohn,cabare,hrenart,sbrabyn,ohatto,hbrandow,dhammontree,kwidrick,ascovel,jskafec,uslavinski,imcbay,wclokecloak,cflenner,hbastidos,lcaudell,gcarlini,opuglisi,nbugtong,hbetterman,lshilling,nfunchess,nlainhart,kconkey,ktuccio,mcontreras,dasiedu,cbotdorf,rchevrette,mgavet,hchaviano,zwinterbottom,fthein,zculp,bdominga,dlargo,hbickford,lrandall,ykimbel,lautovino,cfasone,hdoiel,ediga,hmatonak,fmilsaps,amckinney,mquigg,mvanpelt,daubert,dgiacomazzi,hhysong,svielle,zanderlik,mpizzaro,bromano,kmarzili,uweyand,smullowney,rbernhagen,ajaquess,ekeuper,lbove,greiff,uransford,ewicks,cpentreath,kepps,uhayakawa,tmccamish,rdubuisson,dtashjian,ibreitbart,ffigert,ycostaneda,kmedcaf,fgrashot,tredfearn,nedgin,mrydelek,tsowells,ilamberth,hhartranft,dsharr,oport,areid,bbeckfield,bluellen,fagro,ihegener,sackles,fparness,lvaleriano,faleo,fbielecki,jeuresti,lcavez,nerbach,tschnepel,zkutchera,limbrogno,nkubley,afredin,gwaud,bmoling,rschkade,kfaure,vtresch,ekurter,estockwin,rgoonez,erostad,nrysavy,hhaffey,apurdon,llasher,jholzmiller,ashuey,rbillingsly,osaber,asemons,edurick,tgindhart,svongal,mvedder,jvillaire,dholdaway,bbrenton,lpitek,jjumalon,kjoslyn,cparee,cklem,mcoch,kmcardle,dsteever,nlemma,rfauerbach,wnunziata,fsirianni,dciullo,udatu,cjody,mvas,hkinderknecht,cpencil,rmcstay,tboxx,brodgerson,mfeil,eberkman,gdeblasio,hspiry,ilevian,wdagrella,bharnois,sscheiern,vbigalow,nschmig,pwohlenhaus,uflander,ckodish,amcgraw,cswigert,mcampagnone,inarain,kmcguire,tharr,bdaughenbaugh,garchambeault,bmarszalek,pvirelli,snotari,nspolar,skanjirathinga,fsunderland,mmesidor,lmuehlberger,glafontaine,aferge,hcarrizal,pdurando,gdeyarmond,fmarchi,wstjean,obeaufait,nslaby,dlongbotham,tplatko,jcaroll,isplonskowski,zscammahorn,sstuemke,cnoriego,nsiemonsma,lseabold,cmafnas,dhendon,bfishbeck,gkerens,eklunder,fburrough,ebusk,tmarkus,clouder,cweiss,mpellew,ojerabek,veisenhardt,vwokwicz,tvrooman,rpitter,slerew,dwittlinger,habby,mpanahon,rguinane,zneeb,eyounglas,gcervantez,kbrugal,ycobetto,tkeala,pheathcock,cmellberg,hmiazga,bmicklos,bphou,ngullett,jwinterton,lcremer,jmartha,icoard,ahandy,eparham,gtinnel,wganther,umarbury,fhalon,bsibal,uschweyen,gearnshaw,cbleimehl,omasone,cdeckard,ctetteh,arosel,pmineo,gclapham,jamber,sbonnie,eaguire,jmarugg,ihalford,wdovey,sarndt,gbitar,ovibbert,ewismer,gmilian,rginer,gdaub,showe,hlynema,rtooker,svandewalle,fhain,jlunney,jreigh,kmandolfo,leberhardt,wkhazaleh,nasmar,egrago,ablackstock,lcocherell,pvierthaler,vrunyon,kpalka,ubenken,hmuscaro,jherkenratt,pminnis,bscadden,srubenfield,cnabzdyk,mpytko,gchounlapane,pwademan,nousdahl,pcornn,zmeeker,hpalmquist,jrees,mkofoed,mkibler,lbassin,fplayfair,hmogush,nvyhnal,ileaman,gschaumburg,thoch,wconces,hliverman,gmackinder,rbrisby,isowder,rkraszewski,hzagami,obihl,nhelfinstine,mbravata,thynson,vwaltmann,tlana,ggehrke,pwutzke,zbuscaglia,ewuitschick,hgalavis,ddigerolamo,wmendell,etunby,jkimpton,mheilbrun,laksamit,hvannette,jseen,sgurski,iroiger,lcanestrini,baigner,dminozzi,uazatyan,gjankowiak,bstrede,mstirn,hfludd,mdyce,tbattista,gfaire,gapkin,esproull,gcurnutt,tstalworth,ienglert,hbrehmer,csoomaroo,kaanerud,nlinarez,jeverton,uspittler,prowena,gsantella,oreiss,rcheshier,tpaa,kwirght,gparkersmith,jquicksall,xrahaim,vwisinger,aesbensen,eorsten,imensah,omalvaez,dnegri,wmailey,tyounglas,vtowell,pgrybel,lmauracher,lschollmeier,ithum,umosser,pbeckerdite,hsabol,dhindsman,ugerpheide,gconver,lhuggler,amanganelli,omatula,zhaulk,lkimel,mruppel,egospatrick,kseisler,ehindbaugh,mdecourcey,kbartolet,vcrofton,cdegravelle,ksiering,fvallian,kalguire,dblazejewski,vdesir,tairth,hcusta,mjeon,smccaie,hpolintan,ihimmelwright,fbeatrice,yvdberg,uednilao,vmedici,sskone,dbarriball,ndrumgole,ccyganiewicz,cdrumm,usevera,vsefcovic,mfitzherbert,fberyman,upater,vpiraino,pwashuk,kshippy,bcolorado,cbarlup,cmiramon,kdevincent,mcaram,cbourek,hkohlmeyer,lringuette,lgradilla,slaningham,ksparling,tcrissinger,senrico,dlanois,iyorks,gbolay,rpikes,hcafourek,shaith,fverfaille,btheim,iambrosino,ghann,fkeef,tsearle,tsepulueda,iherrarte,fvinal,sherzberg,iiffert,astrunk,ghelderman,moller,gmassi,oahyou,cjuntunen,mvanbergen,tkelly,eziebert,nhija,sjankauskas,pdech,mmangiamele,clewicki,meconomides,tmccaffity,carguellez,prepasky,amaslyn,kmallach,ejeppesen,hwoodert,dgivliani,nglathar,fwidhalm,kheadlon,ihernan,oshough,nevan,mpilon,mviverette,beon,alat,ktriblett,ivanschaack,vnazzal,lwedner,alienhard,slaudeman,cpalmios,gishii,kpuebla,ascheno,ocrabbs,dledenbach,ebeachem,ideveyra,sspagnuolo,fsymmonds,srees,isteinlicht,bveeneman,myokoyama,agordner,xlantey,broher,bpinedo,psharits,iweibe,nchrisman,htomlinson,cdickes,draymundo,jbielicki,ulanigan,ihanneman,ppeper,ljomes,khovanesian,ibeto,ilacourse,iseipel,iogasawara,jglotzbecker,mferandez,gpomerance,pdulac,mgayden,skoegler,kbattershell,uvanmatre,wvermeulen,ekenady,ikulbida,htsuha,lvanconant,njordon,oosterhouse,tmelland,lspielvogel,bmarlin,bouten,fgoben,bjolly,iyorgey,htilzer,dgosser,gcobane,vpeairs,dloubier,zfarler,fvascones,awhitt,cscullion,nkempon,rgriffies,wconstantino,opizzuti,scocuzza,pgreenier,ueriks,cwank,mdanos,kmisove,ndesautels,hlichota,cgalinol,rlambertus,zvagt,ohoffert,vchevalier,vwabasha,amayorga,mtintle,rbloomstrand,swoodie,gportolese,hriech,ckerska,gvollrath,bdevera,lmadruga,mbeagley,hdyner,fcha,rlatessa,lsivic,mdedon,mcashett,ubynum,lcoulon,cbrechbill,kgremminger,yfrymoyer,pahles,guresti,kmayoras,mbodley,phalkett,kolexa,fsapien,cghianni,oalthouse,mpark,mlenning,gfedewa,imicthell,farquette,nhayer,vglidden,tkhora,mneubacher,esthill,ecolden,nnamanworth,eklein,pgiegerich,smillian,nmccolm,ameisinger,rtole,jsegundo,jknight,behrke,tguinnip,wlynch,tmorr,omcdaid,dfollman,kmosko,mground,pfavolise,dfirpo,aponcedeleon,wenglander,pduitscher,emehta,lyoula,bmadamba,critchie,gloebs,jscheitlin,tsann,tmalecki,okave,dsherard,wdevenish,dmahapatra,redling,venfort,hstreitnatter,tfetherston,jsenavanh,mmerriwether,pbondroff,tabdelal,badair,bhelverson,jlebouf,tfalconeri,sgefroh,mredd,wselim,ikadar,nrybij,eathey,pschrayter,gmings,xeppley,hrapisura,tdonathan,bcoletta,mdickinson,vdolan,pbiggart,ibyles,kcomparoni,jmatty,psundeen,imarungo,cmcanulty,tmcmickle,obenallack,qhanly,saben,owhitelow,dtornow,btempel,agimm,cpluid,ktoni,rlosinger,fnottage,mfaeth,tmurata,fcunard,saycock,mmcchristian,mcasida,kmoesch,kottomaniello,bwynes,emargulis,kbarnthouse,psalesky,mlinardi,fberra,cgaudette,sestergard,afuchs,esheehan,dscheurer,sgropper,jbjorkman,dflore,vbonder,nnickel,klurie,hmateer,lseehafer,cpinela,maustine,zratti,ohove,okveton,mhollings,vrodick,nwescott,mtanzi,ktuner,yschmuff,akraskouskas,lschnorbus,dmcgillen,aziernicki,wleiva,nendicott,kcofrancesco,cmanno,deshmon,adenicola,hlauchaire,mlaverde,kpenale,dmarchizano,pviviani,vemily,agarbett,ohedlund,werrick,imillin,oconerly,wottesen,kmeester,nwiker,nranck,jroman,cspilis,mallmand,yhenriques,nphan,nbuford,nlohmiller,istallcup,hzinda,atollefsrud,spolmer,purquilla,bgavagan,nramones,lnormand,adishaw,jdodge,moser,urosentrance,oclunes,lpeagler,ubieniek,sgirsh,dzurek,hlemon,pwetherwax,wcreggett,kgarced,pthornberry,nmoren,gcukaj,lbuchtel,dcaltabiano,ibuzo,akomsthoeft,upellam,ptraweek,abortignon,ralspach,pcaposole,hcintron,cbartnick,vnery,lfarraj,pwhitmire,kpannunzio,vfeigel,lpintor,tlowers,fsplinter,rfassinger,ofelcher,csever,oolivarez,kbrevitz,ctuzzo,owhelchel,ptoenjes,mskeele,lschenkelberg,tsablea,hloftis,cbelardo,ycerasoli,gmoen,obercier,cfleurantin,hbraim,ihoa,ochasten,fsavela,zborgmeyer,sbemo,mcolehour,vtrumpp,lgandee,atonkin,rpinilla,hsweezer,hwestermark,lbanco,bwinterton,hcowles,ninnella,ehathcock,uholecek,alamour,bguthary,mdimaio,lsous,ecelestin,ademosthenes,ncermeno,vkrug,ngiesler,pdauterman,achhor,hpimpare,epeterson,lfichtner,tgelen,pdischinger,nlatchaw,psabado,ecordas,dpebbles,ckistenmacher,oscarpello,hschelb,nridinger,tvehrs,lpondexter,rgramby,ocalleo,imuehl,istarring,teliades,ctenny,kstachurski,ugreenberg,cpaccione,cgaler,mmattu,opeet,sstough,dlablue,mespinel,sbloise,ohearl,cbrom,krahman,ysnock,vlubic,rmandril,eserrett,gshrode,ksollitto,ilawbaugh,jappleyard,pbascom,rnordby +check "getent group hugegroup | sortgroup" << EOM +hugegroup:*:1006:ablackstock,abortignon,achhor,ademosthenes,adenicola,adishaw,aesbensen,aferge,afredin,afuchs,agarbett,agimm,agordner,ahandy,ajaquess,akertzman,akomsthoeft,akraskouskas,akravetz,alamour,alat,alienhard,amanganelli,amaslyn,amayorga,amccroskey,amcgraw,amckinney,ameisinger,aponcedeleon,apurdon,areid,arosel,ascheno,ascovel,asemons,ashuey,asivley,astrunk,atollefsrud,atonkin,awhitt,aziernicki,badair,baigner,bbeckfield,bbrenton,bcoletta,bcolorado,bdadds,bdaughenbaugh,bdevera,bdominga,behrke,beon,bfishbeck,bgavagan,bguthary,bharnois,bhelverson,bjolly,blovig,bluellen,bmadamba,bmarlin,bmarszalek,bmicklos,bmoling,bouten,bphou,bpinedo,brodgerson,broher,bromano,bscadden,bsibal,bstrede,bswantak,btempel,btheim,bveeneman,bwinterton,bwynes,cabare,carguellez,cbarlup,cbartnick,cbelardo,cbleimehl,cbotdorf,cbourek,cbrechbill,cbrom,ccyganiewicz,cdeckard,cdegravelle,cdickes,cdrumm,cfasone,cflenner,cfleurantin,cgaler,cgalinol,cgaudette,cghianni,charriman,cjody,cjuntunen,ckerska,ckistenmacher,cklem,ckodish,clapenta,clewicki,clouder,cmafnas,cmanno,cmcanulty,cmellberg,cmiramon,cnabzdyk,cnoriego,cpaccione,cpalmios,cparee,cpencil,cpentreath,cpinela,cpluid,critchie,cscullion,csever,csoomaroo,cspilis,cswigert,ctenny,ctetteh,ctuzzo,cwank,cweiss,dasiedu,daubert,dbarriball,dbertels,dblazejewski,dcaltabiano,dciullo,ddeguire,ddigerolamo,denriquez,deshmon,dfirpo,dflore,dfollman,dgiacomazzi,dgivliani,dgosser,dhammontree,dhendon,dhindsman,dholdaway,dlablue,dlanois,dlargo,dledenbach,dlongbotham,dloubier,dmahapatra,dmarchizano,dmcgillen,dminozzi,dnegri,dpebbles,draymundo,dscheurer,dsharr,dsherard,dsteever,dtashjian,dtornow,dtuholski,dwittlinger,dzurek,eaguire,eathey,ebattee,ebeachem,eberkman,ebusk,ecelestin,ecolden,ecordas,ediga,edrinkwater,edurick,egospatrick,egrago,ehathcock,ehindbaugh,ejeppesen,ekalfas,ekenady,ekeuper,eklein,eklunder,ekurter,emanikowski,emargulis,emcquiddy,emehta,eorsten,eparham,epeterson,epoinelli,erathert,erostad,eserrett,esheehan,esonia,esproull,esthill,estockwin,etunby,ewicks,ewilles,ewismer,ewuitschick,eyounglas,eziebert,fagro,faleo,farquette,fbeatrice,fberra,fberyman,fbielecki,fburrough,fcha,fcunard,ffigert,fgoben,fgrashot,fhain,fhalon,fkeef,fmarchi,fmilsaps,fnottage,fparness,fplayfair,fsapien,fsavela,fsirianni,fsplinter,fsunderland,fsymmonds,fthein,fvallian,fvascones,fverfaille,fvinal,fwidhalm,gallanson,gapkin,garchambeault,gbitar,gbolay,gcarlini,gcervantez,gchounlapane,gclapham,gcobane,gconver,gcukaj,gcummer,gcurnutt,gdaub,gdeblasio,gdeyarmond,gdrilling,gearnshaw,gfaire,gfedewa,ggehrke,ggillim,ghann,ghelderman,ghumbles,gishii,gjankowiak,gkerens,glafontaine,gloebs,gmackinder,gmassi,gmilian,gmings,gmoen,gparkersmith,gpomerance,gportolese,greiff,gsantella,gschaumburg,gshrode,gtinnel,guresti,gvollrath,gwaud,habby,hbastidos,hbetterman,hbickford,hbraim,hbrandow,hbrehmer,hbukovsky,hcafourek,hcarrizal,hchaviano,hcintron,hcowles,hcusta,hdoiel,hdyner,hfludd,hgalavis,hhaffey,hhagee,hhartranft,hholyfield,hhysong,hkarney,hkinderknecht,hkippes,hkohlmeyer,hlauchaire,hlemon,hlichota,hliverman,hloftis,hlynema,hmateer,hmatonak,hmiazga,hmogush,hmuscaro,hpalmquist,hpimpare,hpolintan,hrapisura,hrenart,hriech,hsabol,hschelb,hschoepfer,hspiry,hstreitnatter,hsweezer,htilzer,htomlinson,htsuha,hvannette,hveader,hwestermark,hwoodert,hzagami,hzinda,iambrosino,ibeto,ibreitbart,ibuzo,ibyles,ichewning,icoard,ideveyra,ienglert,igizzi,ihalford,ihanneman,ihegener,ihernan,iherrarte,ihimmelwright,ihoa,iiffert,ikadar,ikulbida,ilacourse,ilamberth,ilawbaugh,ileaman,ilevian,imarungo,imcbay,imensah,imicthell,imillin,imuehl,inarain,iogasawara,iroiger,iseipel,isowder,isplonskowski,istallcup,istarring,isteinlicht,ithum,ivanschaack,iweibe,iyorgey,iyorks,jamber,jappleyard,jbielicki,jbjorkman,jcaroll,jdodge,jeuresti,jeverton,jglotzbecker,jherkenratt,jholzmiller,jjumalon,jkimpton,jknight,jlebouf,jlunney,jmartha,jmarugg,jmatty,joligee,jquicksall,jrees,jreigh,jroman,jscheitlin,jseen,jsegundo,jsenavanh,jskafec,jspohn,jsweezy,jvillaire,jwinterton,jzych,kaanerud,kalguire,kbarnthouse,kbartolet,kbattershell,kbrevitz,kbrugal,kcofrancesco,kcomparoni,kconkey,kdevincent,kepps,kfaure,kfend,kgarced,kgremminger,khartness,kheadlon,khovanesian,kjoslyn,klitehiser,klundsten,klurie,kmallach,kmandolfo,kmarzili,kmayoras,kmcardle,kmcguire,kmedcaf,kmeester,kmisove,kmoesch,kmosko,kmuros,kolexa,kottomaniello,kpalka,kpannunzio,kpenale,kpuebla,krahman,kseisler,kshippy,ksiering,ksollitto,ksparling,kstachurski,kthede,ktoni,ktriblett,ktuccio,ktuner,kwidrick,kwinterling,kwirght,laksamit,lautovino,lbanco,lbassin,lbove,lbuchtel,lcanestrini,lcaudell,lcavez,lcocherell,lcoulon,lcremer,leberhardt,lfarraj,lfichtner,lgadomski,lgandee,lgradilla,lhuggler,limbrogno,ljomes,lkimel,llarmore,llasher,lmadruga,lmauracher,lmcgeary,lmichaud,lmuehlberger,lnormand,lparrish,lpeagler,lpintor,lpitek,lpondexter,lrandall,lringuette,lschenkelberg,lschnorbus,lschollmeier,lseabold,lseehafer,lshilling,lsivic,lsobrino,lsous,lspielvogel,lvaleriano,lvanconant,lwedner,lyoula,mallmand,maustine,mbeagley,mbodley,mbravata,mcampagnone,mcaram,mcashett,mcasida,mcoch,mcolehour,mcontreras,mdanos,mdecourcey,mdedon,mdickinson,mdimaio,mdoering,mdyce,meconomides,mespinel,mfaeth,mfeil,mferandez,mfitzherbert,mgavet,mgayden,mground,mheilbrun,mhollings,mjeon,mkibler,mkofoed,mlaverde,mlenning,mlinak,mlinardi,mmangiamele,mmattu,mmcchristian,mmerriwether,mmesidor,mneubacher,moller,moser,mpanahon,mpark,mpellew,mpilon,mpizzaro,mpytko,mquigg,mredd,mrizer,mruppel,mrydelek,mskeele,mstirn,mswogger,mtanzi,mtintle,mvanbergen,mvanpelt,mvas,mvedder,mviverette,myokoyama,nagerton,nasmar,nbuford,nbugtong,ncermeno,nchrisman,nciucci,ndesautels,ndrumgole,nedgin,nendicott,nerbach,nevan,nforti,nfunchess,ngiesler,nglathar,ngrowney,ngullett,nhayer,nhelfinstine,nhija,ninnella,njordon,nkempon,nkubley,nlainhart,nlatchaw,nlemma,nlinarez,nlohmiller,nmccolm,nmoren,nnamanworth,nnickel,nousdahl,nphan,nramones,nranck,nridinger,nriofrio,nrybij,nrysavy,nschmig,nsiemonsma,nslaby,nspolar,nvyhnal,nwescott,nwiker,oahyou,oalthouse,obeaufait,obenallack,obercier,obihl,ocalleo,ochasten,oclunes,oconerly,ocrabbs,oebrani,ofelcher,ohatto,ohearl,ohedlund,ohoffert,ohove,ojerabek,okave,okveton,omalvaez,omasone,omatula,omcdaid,oolivarez,oosterhouse,opeet,opizzuti,opoch,oport,opuglisi,oreiss,osaber,oscarpello,oshough,ovibbert,owhelchel,owhitelow,pahles,pbascom,pbeckerdite,pbiggart,pbondroff,pbrentano,pcaposole,pcornn,pdauterman,pdech,pdischinger,pduitscher,pdulac,pdurando,pfavolise,pgiegerich,pgreenier,pgrybel,phalkett,pheathcock,phyer,pmineo,pminnis,ppedraja,ppeper,pphuaphes,prepasky,prowena,psabado,psalesky,pschrayter,psharits,psiroky,psundeen,pthornberry,ptoenjes,ptraweek,purquilla,pvierthaler,pvirelli,pviviani,pwademan,pwashuk,pwetherwax,pwhitmire,pwohlenhaus,pwutzke,qhanly,ralspach,rbernhagen,rbillingsly,rbloomstrand,rbrisby,rcheshier,rchevrette,rdubs,rdubuisson,redling,rfassinger,rfauerbach,rfidel,rginer,rgoonez,rgramby,rgriffies,rguinane,rheinzmann,rkraszewski,rlambertus,rlatessa,rlosinger,rmandril,rmcstay,rnordby,rpastorin,rpikes,rpinilla,rpitter,rramirez,rrasual,rschkade,rtole,rtooker,saben,sackles,sarndt,saycock,sbemo,sbettridge,sbloise,sbonnie,sbrabyn,scocuzza,sdebry,senrico,sestergard,sgefroh,sgirsh,sgropper,sgunder,sgurski,shaith,sherzberg,showe,sjankauskas,skanjirathinga,skoegler,slaningham,slaudeman,slerew,smccaie,smillian,smullowney,snotari,spolmer,srees,srubenfield,sscheiern,sskone,sskyers,sspagnuolo,sstough,sstuemke,svandewalle,svielle,svogler,svongal,swoodie,tabdelal,tairth,tbagne,tbattista,tboxx,tcacal,tcossa,tcrissinger,tdonathan,teliades,tfalconeri,tfetherston,tgelen,tgindhart,tguinnip,tharr,thelfritz,thoch,thynson,tkeala,tkelly,tkhora,tlana,tlowers,tmalecki,tmarkus,tmccaffity,tmccamish,tmcmickle,tmelland,tmorr,tmurata,tmysinger,tnaillon,tnitzel,tpaa,tplatko,tredfearn,tsablea,tsann,tschnepel,tsearle,tsepulueda,tsowells,tstalworth,tvehrs,tvrooman,tyounglas,ualway,uazatyan,ubenken,ubieniek,ubynum,udatu,uednilao,ueriks,uflander,ugerpheide,ugreenberg,uhayakawa,uholecek,ulanigan,umarbury,umosser,upater,upellam,uransford,urosentrance,uschweyen,usevera,uslavinski,uspittler,uvanmatre,uwalpole,uweyand,vbaldasaro,vbigalow,vbonder,vburton,vchevalier,vcrofton,vdesir,vdolan,veisenhardt,vemily,venfort,vfeigel,vglidden,vkrug,vlubic,vmaynard,vmedici,vnazzal,vnery,vpeairs,vpender,vpiraino,vrodick,vrunyon,vsefcovic,vstirman,vtowell,vtresch,vtrumpp,vwabasha,vwaltmann,vwisinger,vwokwicz,wbrill,wclokecloak,wconces,wconstantino,wcreggett,wdagrella,wdevenish,wdovey,wenglander,werrick,wesguerra,wganther,wkhazaleh,wleiva,wlynch,wmailey,wmendell,wnunziata,wottesen,wselim,wstjean,wtruman,wvalcin,wvermeulen,xeppley,xlantey,xrahaim,yautin,ycerasoli,ycobetto,ycostaneda,yduft,yeven,yfrymoyer,ygockel,yhenriques,ykimbel,yolivier,yschmuff,ysnock,yvdberg,zanderlik,zborgmeyer,zbuscaglia,zculp,zfarler,zhaulk,zkutchera,zmeeker,zneeb,zratti,zscammahorn,zvagt,zwinterbottom EOM ########################################################################### @@ -420,7 +430,7 @@ ecordas:*::::7:2::0 EOM check "getent shadow arthur" << EOM -arthur:*::1000:200:7:2::0 +arthur:*:14302:::7:2::0 EOM # check case-sensitivity |