#include #include #include #include #include #include "ldapconf_defs.h" #include "ldapconf.h" #include #include #include static const char POSIX_PROFILE[]="posix_accounts"; static CONFIG_FILE dellog ( "/var/log/ldap.del.log",help_nil,CONFIGF_OPTIONAL|CONFIGF_MANAGED ,"root","root",0600); static USERS batch_users; static GROUPS batch_groups; static bool batchmode=false; static LDAP *cldap = NULL; static LDAPOBJECT *ldapo; static void showtime (int level, const char *msg, ...) { #if 0 static long long last[10]; struct timeval tv; gettimeofday (&tv,NULL); long long now = tv.tv_sec *(long long)1000000 + tv.tv_usec; if (last[level] != 0){ long long diff = now - last[level]; va_list list; va_start (list,msg); char buf[100]; vsnprintf (buf,sizeof(buf)-1,msg,list); va_end (list); fprintf (stderr,"%*s%s %ld.%06ld\n",level*2,"" ,buf,(long)(diff/1000000),(long)(diff%1000000)); } for (int i=level; i<10; i++) last[i] = now; #endif } static void posix_connect() { if (cldap == NULL){ ldapo = new LDAPOBJECT (POSIX_PROFILE); LDAPPROFILE *bind = &ldapo->bind; cldap = ldap_open (bind->host.get(),bind->port.getval()); if (cldap == NULL){ xconf_error (MSG_U(E_CANTCONNECT ,"Can't connect to LDAP server %s, port %s") ,bind->host.get(),bind->port.get()); }else{ ldap_set_option (cldap,LDAP_OPT_PROTOCOL_VERSION,(void*)&bind->protocol); int code = ldap_bind_s (cldap,bind->dn.get(),bind->pw.get(),LDAP_AUTH_SIMPLE); if (code != LDAP_SUCCESS){ // int err = ldap_result2error (cldap,res,1); xconf_error ("LDAP error1: %s",ldap_err2string(code)); // ldap_end (cldap); cldap = NULL; delete ldapo; ldapo = NULL; } } } ldapo->reset_data(); } /* Search an attribute in a result set */ static const char *posix_lookup (LDAPMessage *m, const char *attr) { const char *ret = ""; char **vals = ldap_get_values (cldap,m,attr); if (vals != NULL) ret = vals[0]; return ret; } #define _TLMP_psearch struct _F_psearch{ #define _F_psearch_onerec(x) void x onerec(LDAPMessage *m, bool &end) virtual _F_psearch_onerec( )=0; }; static int psearch (_F_psearch &c, LDAP *ld, const char *base, int scope, const char *filter, char *attrs[], int attronly) { int ret = -1; int id = -1; while (1){ showtime (1,"ldap_search %s",filter); id = ldap_search (ld,base,scope,filter,attrs,attronly); if (id != -1 || errno != EINTR) break; } showtime (1,"Fin ldap_search %s id=%d",filter,id); if (id == -1){ xconf_error ("LDAP error2: errno=%d %s",errno); }else{ int nrec = 0; bool end = false; while (!end){ LDAPMessage *res = NULL; int code; while (1){ code = ldap_result (ld,id,0,NULL,&res); if (code != -1 || errno != EINTR) break; } if (code == LDAP_RES_SEARCH_ENTRY){ LDAPMessage *m = ldap_first_entry(ld,res); while (!end && m != NULL){ c.onerec (m,end); nrec++; m = ldap_next_entry(ld,m); } ldap_msgfree(res); }else if (code == LDAP_RES_SEARCH_RESULT){ ldap_msgfree (res); ret = nrec; break; }else{ fprintf (stderr,"ldap_result return %d\n",code); ldap_msgfree (res); break; } } } showtime (1,"Fin ldap_result(s) %s",filter); return ret; } static int posix_readusers (USERS *users, USRACC_FILTER *ufilter) { glocal USERS *users = users; posix_connect(); int bnb = batch_users.getnb(); if (batchmode && bnb > 0){ users->neverdelete(); for (int i=0; iadd (batch_users.getitem(i)); } }else if (cldap != NULL){ SSTRING filter,dc,def_filter; def_filter.setfromf ("%s=*",ldapo->primary_key.get()); if (ufilter != NULL){ filter.setfrom ("(&"); if (ufilter->prefix.is_filled()){ filter.appendf("(%s=%s)",ldapo->primary_key.get() ,ufilter->prefix.get()); } if (ufilter->from > 0){ #if 0 filter.appendf ("(uidNumber>=%d)(uidNumber<=%d)" ,ufilter->from,ufilter->to); #else filter.appendf ("(uidnumber=%d)",ufilter->from); #endif } if (ufilter->gecos.is_filled()){ filter.appendf ("(sn=%s)",ufilter->gecos.get()); } if (ufilter->gid != -1){ filter.appendf("(gidNumber=%d)",ufilter->gid); } filter.append(")"); if (filter.cmp("(&)")==0){ filter = def_filter; } }else{ filter = def_filter; } dc.setfromf ("%s,%s",ldapo->dn_prefix.get(),ldapo->bind.base.get()); static char *atname[]={ "uid","userPassword","uidNumber","gidNumber","gecos", "homeDirectory","loginShell", "shadowLastChange","shadowMin","shadowMax","shadowWarning", "shadowInactive","shadowExpire","shadowFlag",NULL }; (cldap,dc.get() ,LDAP_SCOPE_SUBTREE ,filter.get() ,atname ,0); const char *name = posix_lookup (m,"uid"); // showtime (2,"split user %d",name); const char *passwd = posix_lookup (m,"userPassword"); const char *uidstr = posix_lookup (m,"uidNumber"); const char *gidstr = posix_lookup (m,"gidNumber"); SSTRING gecosbuf; const char *gecos = utf_convfromutf(posix_lookup (m,"gecos"),gecosbuf); const char *home = posix_lookup (m,"homeDirectory"); const char *shell = posix_lookup (m,"loginShell"); const char *shadowLastChange = posix_lookup(m,"shadowLastChange"); const char *shadowMin = posix_lookup(m,"shadowMin"); const char *shadowMax = posix_lookup(m,"shadowMax"); const char *shadowWarning = posix_lookup(m,"shadowWarning"); const char *shadowInactive = posix_lookup(m,"shadowInactive"); const char *shadowExpire = posix_lookup(m,"shadowExpire"); //const char *shadowFlag = posix_lookup(m,"shadowFlag"); glocal.users->add (new USER(name,passwd ,atoi(uidstr),atoi(gidstr) ,gecos,home,shell)); #if 1 glocal.users->addshadow(new SHADOW(name,passwd ,shadowLastChange ,shadowMin,shadowMax,shadowWarning ,shadowExpire,shadowInactive)); #endif if (batchmode){ users->neverdelete(); // We keep a copy to speed up later request for (int i=0; igetnb(); i++){ batch_users.add (users->getitem(i)); } } } return 0; } /* Read some fields associated to a user. Return -1 if any error. */ int posix_read (const char *user, SSTRING_KEYS &tb) { int ret = -1; posix_connect(); if (cldap != NULL){ SSTRING filter,dc; filter.setfromf ("%s=%s",ldapo->primary_key.get(),user); dc.setfromf ("%s,%s",ldapo->dn_prefix.get(),ldapo->bind.base.get()); int nb = tb.getnb(); const char *atname[nb+1]; for (int i=0; iget(); } atname[nb] = NULL; for (int i=0; isetobjval (""); } glocal SSTRING_KEYS *tb = &tb; ret = (cldap,dc.get() ,LDAP_SCOPE_SUBTREE ,filter.get() ,(char**)atname ,0); int nb = glocal.tb->size(); for (int i=0; igetitem(i); const char *val = posix_lookup (m,k->get()); SSTRING buf; utf_convfromutf (val,buf); // fprintf (stderr,"conv key %s :%s: :%s:\n",k->get(),val,buf.get()); k->setobjval (utf_convfromutf(val,buf)); } } return ret; } static int posix_filluser (USERS *users, const char *name, int uid) { USRACC_FILTER filter; filter.prefix.setfrom (name); if (uid != -1){ filter.from = uid; filter.to = uid+1; } return posix_readusers (users,&filter); } static int posix_readgroups (GROUPS *groups) { int ret = -1; posix_connect(); if (cldap != NULL){ SSTRING dc,filter; filter.setfrom("cn=*"); dc.setfromf("%s,%s",ldapo->group_prefix.get(),ldapo->bind.base.get()); static char *atname[]={ "cn","gidNumber","memberUid", NULL }; glocal GROUPS *groups = groups; ret = (cldap,dc.get() ,LDAP_SCOPE_SUBTREE ,filter.get() ,atname ,0); const char *name = posix_lookup (m,"cn"); const char *gidstr = posix_lookup (m,"gidNumber"); char **members = ldap_get_values (cldap,m,"memberUid"); GROUP *grp = new GROUP(name,"*",atoi(gidstr),members); ldap_value_free (members); glocal.groups->add (grp); } return ret; } static int posix_doflush (const char *command) { int ret = 0; const char *tmpfile = ldapo->gettmpfile(); // fprintf (stderr,"file_exist %s %d\n",tmpfile,file_exist(tmpfile)); if (file_exist(tmpfile)){ ldapo->command_line.setfromf("-f %s",tmpfile); SSTRINGS resmsg; ret = ldapo->command (command,resmsg); /* Remove tmp entry file */ unlink(tmpfile); } batch_users.remove_all(); batch_groups.remove_all(); return ret; } static int posix_doflush () { return posix_doflush ("ldapadd"); } static int posix_do (const char *command) { int ret = -1; ldapo->export_ldif (ldapo->gettmpfile(),batchmode); if (batchmode){ ret = 0; }else{ ret = posix_doflush (command); } return ret; } static int group_fct_mod(GROUP *grp); static void posix_updatemembers(const USER *user) { const char *name = user->getname(); // We have to update the various group memberships SSTRINGS tbgr; str_splitline (user->getaltgrs(),' ',tbgr); GROUPS groups; for (int i=0; igetmembers(); bool is_member = tbgr.lookup(g->getname()) != -1; bool was_member = members->lookup(name)!=-1; if (is_member != was_member){ if (is_member){ g->addmember(name); }else{ g->delmember(name); } group_fct_mod (g); } } } static ARRAY_OBJS tbconf; /* Add values managed by the co-managers */ void posix_addcomng(FIELD_DEFS &db) { tbconf.neverdelete(); tbconf.remove (&db); tbconf.add (&db); } void posix_delcomng(FIELD_DEFS &db) { tbconf.remove (&db); } static void posix_findextra_ou (SSTRING &ou) { for (int i=0; ou.is_empty() && isize(); j++){ FIELD_DEF *k = db->getitem(j); if (k->id.cmp("ou")==0){ ou.setfromf (",ou=%s",k->getvalstr()); break; } } } } static int posix_finddn(const USER *user, SSTRING &dn) { glocal int ret = -1; glocal SSTRING *dn = &dn; posix_connect(); if (cldap != NULL){ posix_doflush(); /* The account may be recorded at various depth (using nested OU) in LDAP. We can't guess the account DN from the profile base DN. So we must perform a search using the base dn. We assume here that sub OU nevertheless contain unique IDs (cn). */ SSTRING dc,filter; filter.setfromf("cn=%s",user->getname()); dc.setfromf("%s,%s",ldapo->dn_prefix.get(),ldapo->bind.base.get()); (cldap,dc.get() ,LDAP_SCOPE_SUBTREE ,filter.get() ,NULL ,0); const char *dn = ldap_get_dn (cldap,m); glocal.dn->setfrom(dn); glocal.ret = 0; } return glocal.ret; } static int user_fct_del(const USER *user, const SHADOW *) { glocal int ret = -1; posix_connect(); if (cldap != NULL){ posix_doflush(); /* The account may be recorded at various depth (using nested OU) in LDAP. We can't guess the account DN from the profile base DN. So we must perform a search using the base dn. We assume here that sub OU nevertheless contain unique IDs (cn). */ SSTRING dc,filter; filter.setfromf("cn=%s",user->getname()); dc.setfromf("%s,%s",ldapo->dn_prefix.get(),ldapo->bind.base.get()); (cldap,dc.get() ,LDAP_SCOPE_SUBTREE ,filter.get() ,NULL ,0); const char *dn = ldap_get_dn (cldap,m); ldapo->dn.setfrom(dn); glocal.ret = ldapo->del(); if (glocal.ret == 0){ // Record the ldiff of the deleted record in the log if (dellog.exist()){ FILE_CFG *fout = dellog.fopen ("a"); if (fout != NULL){ fprintf (fout,"##### %s\n",dn); fprintf (fout,"dn: %s\n",dn); BerElement *ber; const char *attr = ldap_first_attribute(cldap,m,&ber); while (attr != NULL){ char **vals = ldap_get_values (cldap,m,attr); for (int i=0; vals[i] != NULL; i++){ fprintf (fout,"%s: %s\n",attr,vals[i]); } ldap_value_free (vals); attr = ldap_next_attribute (cldap,m,ber); } fprintf (fout,"\n"); fclose (fout); ber_free (ber,0); } } } } return glocal.ret; } static void posix_add_comngvals() { for (int i=0; isaveval (*ldapo); } } static int user_fct_add(USER *user, SHADOW *sha, bool) { posix_connect(); const char *name = user->getname(); SSTRING ou; posix_findextra_ou (ou); ldapo->dn.setfromf("dn: %s=%s%s,%s,%s" ,ldapo->primary_key.get(),name ,ou.get() ,ldapo->dn_prefix.get(),ldapo->bind.base.get()); ldapo->addclasses(); posix_add_comngvals(); if (ldapo->at_get("sn")==NULL) ldapo->at_set ("sn",name); ldapo->at_set("uid",name); ldapo->at_set("cn",name); uid_t uid = user->getuid(); if (uid == (uid_t)-1){ uid = ldap_get_free_uidnumber(POSIX_PROFILE); user->setuid (uid); } ldapo->at_set("uidNumber",uid); gid_t gid = user->getgid(); if (gid == (gid_t)-1){ if (policies_privgroup()){ gid = uid; }else{ gid = 100; } user->setgid (gid); } ldapo->at_set("gidNumber",gid); ldapo->at_set("homeDirectory",user->gethome()); ldapo->at_set("loginShell",user->getshell()); ldapo->at_set("Gecos",user->getgecos()); ldapo->at_set("rid",uid*2+1000); if (sha != NULL){ ldapo->at_set ("shadowLastChange",sha->getlastchange()); ldapo->at_set ("shadowMin",sha->getmaychange()); ldapo->at_set ("shadowMax",sha->getmustchange()); ldapo->at_set ("shadowWarning",sha->getwarn()); ldapo->at_set ("shadowInactive",sha->getdisable()); ldapo->at_set ("shadowExpire",sha->getexpire()); } int ret = posix_do ("ldapadd"); const char *alt = user->getaltgrs(); if (alt != NULL && alt[0] != '\0') posix_updatemembers (user); if (batchmode) batch_users.add (user); return ret; } static int user_fct_mod(const USER *user, const SHADOW *sha) { int ret = -1; SSTRING dn; if (posix_finddn(user,dn)!=-1){ const char *name = user->getname(); ldapo->addclasses(); ldapo->dn.setfromf ("dn: %s",dn.get()); posix_add_comngvals(); ldapo->at_set("uid",name); ldapo->at_set("cn",name); ldapo->at_set("uidNumber",user->getuid()); ldapo->at_set("gidNumber",user->getgid()); ldapo->at_set("homeDirectory",user->gethome()); ldapo->at_set("loginShell",user->getshell()); ldapo->at_set("Gecos",user->getgecos()); ldapo->at_set("rid",user->getuid()*2+1000); if (sha != NULL){ ldapo->at_set ("shadowLastChange",sha->getlastchange()); ldapo->at_set ("shadowMin",sha->getmaychange()); ldapo->at_set ("shadowMax",sha->getmustchange()); ldapo->at_set ("shadowWarning",sha->getwarn()); ldapo->at_set ("shadowInactive",sha->getdisable()); ldapo->at_set ("shadowExpire",sha->getexpire()); } ret = posix_do ("ldapmodify"); posix_updatemembers (user); posix_doflush(); } return ret; } static int group_fct_del(GROUP *grp) { posix_connect(); ldapo->dn.setfromf("cn=%s,%s,%s" ,grp->getname() ,ldapo->group_prefix.get(),ldapo->bind.base.get()); return ldapo->del(); } static void posix_setmembers (LDAPOBJECT &ldap, GROUP *grp) { const SSTRINGS *members = grp->getmembers(); for (int i=0; igetnb(); i++){ ldap.at_add ("memberUid",members->getitem(i)->get()); } } static int group_fct_add(GROUP *grp) { posix_connect(); const char *name = grp->getname(); ldapo->dn.setfromf("dn: cn=%s,%s,%s" ,name ,ldapo->group_prefix.get(),ldapo->bind.base.get()); ldapo->oc_add("posixGroup"); ldapo->at_set("cn",name); gid_t gid = grp->getgid(); if (gid == (gid_t)-1){ gid = ldap_get_free_gidnumber(POSIX_PROFILE); grp->setgid (gid); } ldapo->at_set("gidNumber",gid); posix_setmembers (*ldapo,grp); return posix_do ("ldapadd"); } static int group_fct_mod(GROUP *grp) { posix_connect(); const char *name = grp->getname(); ldapo->dn.setfromf("dn: cn=%s,%s,%s" ,name ,ldapo->group_prefix.get(),ldapo->bind.base.get()); ldapo->at_set("cn",name); ldapo->at_set("gidNumber",grp->getgid()); posix_setmembers (*ldapo,grp); return posix_do ("ldapmodify"); } extern int (*users_fct_read)(USERS *, USRACC_FILTER *); extern int (*users_fct_fill)(USERS *, const char *, int); extern int (*groups_fct_read)(GROUPS *); void posix_init() { users_fct_read = posix_readusers; users_fct_fill = posix_filluser; groups_fct_read = posix_readgroups; users_sethook (user_fct_del,user_fct_add,user_fct_mod); groups_sethook (group_fct_del,group_fct_add,group_fct_mod); } /* Record the operation mode. In batch mode, we expect many database updates in a row, so we delayed the update on ldap and keep in memory few things to avoid request to ldap each time. */ void posix_batchmode (bool mode) { //batchmode = mode; //if (!batchmode) posix_doflush(); }