windows_protocol_util.c 154 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836
  1. /** BEGIN COPYRIGHT BLOCK
  2. * This Program is free software; you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation; version 2 of the License.
  5. *
  6. * This Program is distributed in the hope that it will be useful, but WITHOUT
  7. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  8. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
  9. *
  10. * You should have received a copy of the GNU General Public License along with
  11. * this Program; if not, write to the Free Software Foundation, Inc., 59 Temple
  12. * Place, Suite 330, Boston, MA 02111-1307 USA.
  13. *
  14. * In addition, as a special exception, Red Hat, Inc. gives You the additional
  15. * right to link the code of this Program with code not covered under the GNU
  16. * General Public License ("Non-GPL Code") and to distribute linked combinations
  17. * including the two, subject to the limitations in this paragraph. Non-GPL Code
  18. * permitted under this exception must only link to the code of this Program
  19. * through those well defined interfaces identified in the file named EXCEPTION
  20. * found in the source code files (the "Approved Interfaces"). The files of
  21. * Non-GPL Code may instantiate templates or use macros or inline functions from
  22. * the Approved Interfaces without causing the resulting work to be covered by
  23. * the GNU General Public License. Only Red Hat, Inc. may make changes or
  24. * additions to the list of Approved Interfaces. You must obey the GNU General
  25. * Public License in all respects for all of the Program code and other code used
  26. * in conjunction with the Program except the Non-GPL Code covered by this
  27. * exception. If you modify this file, you may extend this exception to your
  28. * version of the file, but you are not obligated to do so. If you do not wish to
  29. * provide this exception without modification, you must delete this exception
  30. * statement from your version and license this file solely under the GPL without
  31. * exception.
  32. *
  33. *
  34. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  35. * Copyright (C) 2005 Red Hat, Inc.
  36. * All rights reserved.
  37. * END COPYRIGHT BLOCK **/
  38. #ifdef HAVE_CONFIG_H
  39. # include <config.h>
  40. #endif
  41. /* repl5_protocol_util.c */
  42. /*
  43. Code common to both incremental and total protocols.
  44. */
  45. #include "repl5.h"
  46. #include "repl5_prot_private.h"
  47. #include "windowsrepl.h"
  48. #include "slap.h"
  49. #include <unicode/ustring.h> /* UTF8 conversion */
  50. int ruv_private_new( RUV **ruv, RUV *clone );
  51. #ifdef FOR_DEBUGGING
  52. static Slapi_Entry* windows_entry_already_exists(Slapi_Entry *e);
  53. static void extract_guid_from_entry_bv(Slapi_Entry *e, const struct berval **bv);
  54. #endif
  55. static void windows_map_mods_for_replay(Private_Repl_Protocol *prp,LDAPMod **original_mods, LDAPMod ***returned_mods, int is_user, char** password);
  56. static int is_subject_of_agreement_local(const Slapi_Entry *local_entry,const Repl_Agmt *ra);
  57. static int windows_create_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *original_entry, Slapi_DN *remote_sdn, Slapi_Entry **remote_entry, char** password);
  58. static int windows_get_local_entry(const Slapi_DN* local_dn,Slapi_Entry **local_entry);
  59. static int windows_get_local_entry_by_uniqueid(Private_Repl_Protocol *prp,const char* uniqueid,Slapi_Entry **local_entry, int is_global);
  60. static int windows_get_local_tombstone_by_uniqueid(Private_Repl_Protocol *prp,const char* uniqueid,Slapi_Entry **local_entry);
  61. static int windows_search_local_entry_by_uniqueid(Private_Repl_Protocol *prp, const char *uniqueid, char ** attrs, Slapi_Entry **ret_entry, int tombstone, void * component_identity, int is_global);
  62. static int map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *missing_entry, int want_guid);
  63. static char* extract_ntuserdomainid_from_entry(Slapi_Entry *e);
  64. static char* extract_container(const Slapi_DN *entry_dn, const Slapi_DN *suffix_dn);
  65. static int windows_get_remote_entry (Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,Slapi_Entry **remote_entry);
  66. static int windows_get_remote_tombstone(Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,Slapi_Entry **remote_entry);
  67. static int windows_reanimate_tombstone(Private_Repl_Protocol *prp, const Slapi_DN* tombstone_dn, const char* new_dn);
  68. static const char* op2string (int op);
  69. static int is_subject_of_agreement_remote(Slapi_Entry *e, const Repl_Agmt *ra);
  70. static int map_entry_dn_inbound(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra);
  71. static int windows_update_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,Slapi_Entry *local_entry);
  72. static int is_guid_dn(Slapi_DN *remote_dn);
  73. static int map_windows_tombstone_dn(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *exists);
  74. static int windows_check_mods_for_rdn_change(Private_Repl_Protocol *prp, LDAPMod **original_mods,
  75. Slapi_Entry *local_entry, Slapi_DN *remote_dn, char **newrdn);
  76. static int windows_get_superior_change(Private_Repl_Protocol *prp, Slapi_DN *local_dn, Slapi_DN *remote_dn, char **newsuperior, int to_windows);
  77. /* Controls the direction of flow for mapped attributes */
  78. typedef enum mapping_types {
  79. bidirectional,
  80. fromwindowsonly,
  81. towindowsonly,
  82. disabled
  83. } mapping_types;
  84. /* Controls if we sync the attibute always, or only when we're creating new entries */
  85. /* Used for attributes like samaccountname, where we want to fill it in on a new entry, but
  86. * we never want to change it on an existing entry */
  87. typedef enum creation_types {
  88. always,
  89. createonly
  90. } creation_types;
  91. typedef enum attr_types {
  92. normal,
  93. dnmap
  94. } attr_types;
  95. typedef struct _windows_attribute_map
  96. {
  97. char *windows_attribute_name;
  98. char *ldap_attribute_name;
  99. mapping_types map_type;
  100. creation_types create_type;
  101. attr_types attr_type;
  102. } windows_attribute_map;
  103. /* List of attributes that are common to AD and LDAP, so we simply copy them over in both directions */
  104. static char* windows_user_matching_attributes[] =
  105. {
  106. "description",
  107. "destinationIndicator",
  108. "facsimileTelephoneNumber",
  109. "givenName",
  110. "homePhone",
  111. "homePostalAddress",
  112. "initials",
  113. "l",
  114. "mail",
  115. "mobile",
  116. "o",
  117. "ou",
  118. "pager",
  119. "physicalDeliveryOfficeName",
  120. "postOfficeBox",
  121. "postalAddress",
  122. "postalCode",
  123. "registeredAddress",
  124. "sn",
  125. "st",
  126. "telephoneNumber",
  127. "teletexTerminalIdentifier",
  128. "telexNumber",
  129. "title",
  130. "userCertificate",
  131. "x121Address",
  132. NULL
  133. };
  134. static char* windows_group_matching_attributes[] =
  135. {
  136. "description",
  137. "destinationIndicator",
  138. "facsimileTelephoneNumber",
  139. "givenName",
  140. "homePhone",
  141. "homePostalAddress",
  142. "initials",
  143. "l",
  144. "mail",
  145. "manager",
  146. "mobile",
  147. "o",
  148. "ou",
  149. "pager",
  150. "physicalDeliveryOfficeName",
  151. "postOfficeBox",
  152. "postalAddress",
  153. "postalCode",
  154. "preferredDeliveryMethod",
  155. "registeredAddress",
  156. "sn",
  157. "st",
  158. "telephoneNumber",
  159. "teletexTerminalIdentifier",
  160. "telexNumber",
  161. "title",
  162. "userCertificate",
  163. "x121Address",
  164. NULL
  165. };
  166. /* List of attributes that are single-valued in AD, but multi-valued in DS */
  167. static char * windows_single_valued_attributes[] =
  168. {
  169. "facsimileTelephoneNumber",
  170. "givenName",
  171. "homePhone",
  172. "homePostalAddress",
  173. "initials",
  174. "l",
  175. "mail",
  176. "mobile",
  177. "pager",
  178. "physicalDeliveryOfficeName",
  179. "postalCode",
  180. "sn",
  181. "st",
  182. "street",
  183. FAKE_STREET_ATTR_NAME,
  184. "streetAddress",
  185. "telephoneNumber",
  186. "title",
  187. NULL
  188. };
  189. /* List of attributes that are common to AD and LDAP, so we simply copy them over in both directions */
  190. static char* nt4_user_matching_attributes[] =
  191. {
  192. "description",
  193. NULL
  194. };
  195. static char* nt4_group_matching_attributes[] =
  196. {
  197. "description",
  198. NULL
  199. };
  200. static windows_attribute_map user_attribute_map[] =
  201. {
  202. { "homeDirectory", "ntUserHomeDir", bidirectional, always, normal},
  203. { "scriptPath", "ntUserScriptPath", bidirectional, always, normal},
  204. { "lastLogon", "ntUserLastLogon", fromwindowsonly, always, normal},
  205. { "lastLogoff", "ntUserLastLogoff", fromwindowsonly, always, normal},
  206. { "accountExpires", "ntUserAcctExpires", bidirectional, always, normal},
  207. { "codePage", "ntUserCodePage", bidirectional, always, normal},
  208. { "logonHours", "ntUserLogonHours", bidirectional, always, normal},
  209. { "maxStorage", "ntUserMaxStorage", bidirectional, always, normal},
  210. { "profilePath", "ntUserProfile", bidirectional, always, normal},
  211. /* IETF schema has 'street' and 'streetaddress' as aliases, but Microsoft does not */
  212. { "streetAddress", "street", towindowsonly, always, normal},
  213. { FAKE_STREET_ATTR_NAME, "street", fromwindowsonly, always, normal},
  214. { "userParameters", "ntUserParms", bidirectional, always, normal},
  215. { "userWorkstations", "ntUserWorkstations", bidirectional, always, normal},
  216. { "sAMAccountName", "ntUserDomainId", bidirectional, always, normal},
  217. /* AD uses cn as it's naming attribute. We handle it as a special case */
  218. { "cn", "cn", towindowsonly, createonly, normal},
  219. /* However, it isn't a naming attribute in DS (we use uid) so it's safe to accept changes inbound */
  220. { "name", "cn", fromwindowsonly, always, normal},
  221. { "manager", "manager", bidirectional, always, dnmap},
  222. { "seealso", "seealso", bidirectional, always, dnmap},
  223. {NULL, NULL, -1}
  224. };
  225. static windows_attribute_map group_attribute_map[] =
  226. {
  227. { "groupType", "ntGroupType", bidirectional, createonly, normal},
  228. { "sAMAccountName", "ntUserDomainId", bidirectional, always, normal},
  229. /* IETF schema has 'street' and 'streetaddress' as aliases, but Microsoft does not */
  230. { "streetAddress", "street", towindowsonly, always, normal},
  231. { FAKE_STREET_ATTR_NAME, "street", fromwindowsonly, always, normal},
  232. { "member", "uniquemember", bidirectional, always, dnmap},
  233. {NULL, NULL, -1}
  234. };
  235. /*
  236. * Notes on differences for NT4:
  237. * 1. NT4 returns the SID value in the objectGUID attribute value.
  238. * The SID has variable length and does not match the length of a GUID.
  239. * 2. NT4 currently never generates tombstones. If it did, we'd need to parse the
  240. * different form of the GUID in the tombstone DNs.
  241. * 3. NT4 Does not implement the dirsync control. We always get all users and groups.
  242. * 4. NT4 generates and expects DNs with samaccountname as the RDN, not cn.
  243. * 5. NT4 handles the DN=<GUID> (remember that the '<' '>' characters are included!) DN form
  244. * for modifies and deletes, provided we use the value it gave us in the objectGUID attribute (which is actually the SID).
  245. * 6. NT4 has less and different schema from AD. For example users in NT4 have no firstname/lastname, only an optional 'description'.
  246. */
  247. /*
  248. * When we get an error from an LDAP operation, we call this
  249. * function to decide if we should just keep replaying
  250. * updates, or if we should stop, back off, and try again
  251. * later.
  252. * Returns PR_TRUE if we shoould keep going, PR_FALSE if
  253. * we should back off and try again later.
  254. *
  255. * In general, we keep going if the return code is consistent
  256. * with some sort of bug in URP that causes the consumer to
  257. * emit an error code that it shouldn't have, e.g. LDAP_ALREADY_EXISTS.
  258. *
  259. * We stop if there's some indication that the server just completely
  260. * failed to process the operation, e.g. LDAP_OPERATIONS_ERROR.
  261. */
  262. PRBool
  263. windows_ignore_error_and_keep_going(int error)
  264. {
  265. int return_value = PR_FALSE;
  266. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_ignore_error_and_keep_going\n", 0, 0, 0 );
  267. switch (error)
  268. {
  269. /* Cases where we keep going */
  270. case LDAP_SUCCESS:
  271. case LDAP_NO_SUCH_ATTRIBUTE:
  272. case LDAP_UNDEFINED_TYPE:
  273. case LDAP_CONSTRAINT_VIOLATION:
  274. case LDAP_TYPE_OR_VALUE_EXISTS:
  275. case LDAP_INVALID_SYNTAX:
  276. case LDAP_NO_SUCH_OBJECT:
  277. case LDAP_INVALID_DN_SYNTAX:
  278. case LDAP_IS_LEAF:
  279. case LDAP_INSUFFICIENT_ACCESS:
  280. case LDAP_NAMING_VIOLATION:
  281. case LDAP_OBJECT_CLASS_VIOLATION:
  282. case LDAP_NOT_ALLOWED_ON_NONLEAF:
  283. case LDAP_NOT_ALLOWED_ON_RDN:
  284. case LDAP_ALREADY_EXISTS:
  285. case LDAP_NO_OBJECT_CLASS_MODS:
  286. return_value = PR_TRUE;
  287. break;
  288. /* Cases where we stop and retry */
  289. case LDAP_OPERATIONS_ERROR:
  290. case LDAP_PROTOCOL_ERROR:
  291. case LDAP_TIMELIMIT_EXCEEDED:
  292. case LDAP_SIZELIMIT_EXCEEDED:
  293. case LDAP_STRONG_AUTH_NOT_SUPPORTED:
  294. case LDAP_STRONG_AUTH_REQUIRED:
  295. case LDAP_PARTIAL_RESULTS:
  296. case LDAP_REFERRAL:
  297. case LDAP_ADMINLIMIT_EXCEEDED:
  298. case LDAP_UNAVAILABLE_CRITICAL_EXTENSION:
  299. case LDAP_CONFIDENTIALITY_REQUIRED:
  300. case LDAP_SASL_BIND_IN_PROGRESS:
  301. case LDAP_INAPPROPRIATE_MATCHING:
  302. case LDAP_ALIAS_PROBLEM:
  303. case LDAP_ALIAS_DEREF_PROBLEM:
  304. case LDAP_INAPPROPRIATE_AUTH:
  305. case LDAP_INVALID_CREDENTIALS:
  306. case LDAP_BUSY:
  307. case LDAP_UNAVAILABLE:
  308. case LDAP_UNWILLING_TO_PERFORM:
  309. case LDAP_LOOP_DETECT:
  310. case LDAP_SORT_CONTROL_MISSING:
  311. case LDAP_INDEX_RANGE_ERROR:
  312. case LDAP_RESULTS_TOO_LARGE:
  313. case LDAP_AFFECTS_MULTIPLE_DSAS:
  314. case LDAP_OTHER:
  315. case LDAP_SERVER_DOWN:
  316. case LDAP_LOCAL_ERROR:
  317. case LDAP_ENCODING_ERROR:
  318. case LDAP_DECODING_ERROR:
  319. case LDAP_TIMEOUT:
  320. case LDAP_AUTH_UNKNOWN:
  321. case LDAP_FILTER_ERROR:
  322. case LDAP_USER_CANCELLED:
  323. case LDAP_PARAM_ERROR:
  324. case LDAP_NO_MEMORY:
  325. case LDAP_CONNECT_ERROR:
  326. case LDAP_NOT_SUPPORTED:
  327. case LDAP_CONTROL_NOT_FOUND:
  328. case LDAP_NO_RESULTS_RETURNED:
  329. case LDAP_MORE_RESULTS_TO_RETURN:
  330. case LDAP_CLIENT_LOOP:
  331. case LDAP_REFERRAL_LIMIT_EXCEEDED:
  332. return_value = PR_FALSE;
  333. break;
  334. }
  335. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_ignore_error_and_keep_going\n", 0, 0, 0 );
  336. return return_value;
  337. }
  338. static const char*
  339. op2string(int op)
  340. {
  341. LDAPDebug( LDAP_DEBUG_TRACE, "=> op2string\n", 0, 0, 0 );
  342. LDAPDebug( LDAP_DEBUG_TRACE, "<= op2string\n", 0, 0, 0 );
  343. switch (op) {
  344. case SLAPI_OPERATION_ADD:
  345. return "add";
  346. case SLAPI_OPERATION_MODIFY:
  347. return "modify";
  348. case SLAPI_OPERATION_DELETE:
  349. return "delete";
  350. case SLAPI_OPERATION_MODRDN:
  351. return "rename";
  352. case SLAPI_OPERATION_EXTENDED:
  353. return "extended";
  354. }
  355. return "unknown";
  356. }
  357. static void
  358. windows_dump_entry(const char *string, Slapi_Entry *e)
  359. {
  360. int length = 0;
  361. char *buffer = NULL;
  362. if (slapi_is_loglevel_set(SLAPI_LOG_REPL))
  363. {
  364. buffer = slapi_entry2str(e,&length);
  365. slapi_log_error(SLAPI_LOG_REPL, NULL, "Windows sync entry: %s %s\n", string, buffer);
  366. if (buffer)
  367. {
  368. slapi_ch_free_string(&buffer);
  369. }
  370. }
  371. }
  372. static void
  373. map_dn_values(Private_Repl_Protocol *prp,Slapi_ValueSet *original_values, Slapi_ValueSet **mapped_values, int to_windows, int return_originals)
  374. {
  375. Slapi_ValueSet *new_vs = NULL;
  376. Slapi_Value *original_value = NULL;
  377. int retval = 0;
  378. int i = 0;
  379. /* Set the keep raw entry flag to avoid overwriting the existing raw entry. */
  380. windows_private_set_keep_raw_entry(prp->agmt, 1);
  381. /* For each value: */
  382. i= slapi_valueset_first_value(original_values,&original_value);
  383. while ( i != -1 ) {
  384. int is_ours = 0;
  385. char *new_dn_string = NULL;
  386. const char *original_dn_string = NULL;
  387. int original_dn_string_length = 0;
  388. Slapi_DN *original_dn = NULL;
  389. original_dn_string = slapi_value_get_string(original_value);
  390. /* Sanity check the data was a valid string */
  391. original_dn_string_length = slapi_value_get_length(original_value);
  392. if (0 == original_dn_string_length) {
  393. slapi_log_error(SLAPI_LOG_REPL, NULL, "map_dn_values: length of dn is 0\n");
  394. }
  395. /* Make a sdn from the string */
  396. original_dn = slapi_sdn_new_dn_byref(original_dn_string);
  397. if (to_windows)
  398. {
  399. Slapi_Entry *local_entry = NULL;
  400. /* Try to get the local entry */
  401. retval = windows_get_local_entry(original_dn,&local_entry);
  402. if (0 == retval && local_entry)
  403. {
  404. int missing_entry = 0;
  405. Slapi_DN *remote_dn = NULL;
  406. /* Now map the DN */
  407. is_ours = is_subject_of_agreement_local(local_entry,prp->agmt);
  408. if (is_ours)
  409. {
  410. map_entry_dn_outbound(local_entry,&remote_dn,prp,&missing_entry, 0 /* don't want GUID form here */);
  411. if (remote_dn)
  412. {
  413. if (!missing_entry)
  414. {
  415. /* Success */
  416. if (return_originals)
  417. {
  418. new_dn_string = slapi_ch_strdup(slapi_sdn_get_dn(slapi_entry_get_sdn_const(local_entry)));
  419. } else
  420. {
  421. new_dn_string = slapi_ch_strdup(slapi_sdn_get_dn(remote_dn));
  422. }
  423. }
  424. slapi_sdn_free(&remote_dn);
  425. } else
  426. {
  427. slapi_log_error(SLAPI_LOG_REPL, NULL, "map_dn_values: no remote dn found for %s\n", original_dn_string);
  428. }
  429. } else
  430. {
  431. slapi_log_error(SLAPI_LOG_REPL, NULL, "map_dn_values: this entry is not ours %s\n", original_dn_string);
  432. }
  433. } else {
  434. slapi_log_error(SLAPI_LOG_REPL, NULL, "map_dn_values: no local entry found for %s\n", original_dn_string);
  435. }
  436. if (local_entry)
  437. {
  438. slapi_entry_free(local_entry);
  439. local_entry = NULL;
  440. }
  441. } else
  442. {
  443. Slapi_Entry *remote_entry = NULL;
  444. Slapi_DN *local_dn = NULL;
  445. /* Try to get the remote entry */
  446. retval = windows_get_remote_entry(prp,original_dn,&remote_entry);
  447. if (remote_entry && 0 == retval)
  448. {
  449. is_ours = is_subject_of_agreement_remote(remote_entry,prp->agmt);
  450. if (is_ours)
  451. {
  452. retval = map_entry_dn_inbound(remote_entry,&local_dn,prp->agmt);
  453. if (0 == retval && local_dn)
  454. {
  455. if (return_originals)
  456. {
  457. new_dn_string = slapi_ch_strdup(slapi_sdn_get_dn(slapi_entry_get_sdn_const(remote_entry)));
  458. } else
  459. {
  460. new_dn_string = slapi_ch_strdup(slapi_sdn_get_dn(local_dn));
  461. }
  462. slapi_sdn_free(&local_dn);
  463. } else
  464. {
  465. slapi_log_error(SLAPI_LOG_REPL, NULL, "map_dn_values: no local dn found for %s\n", original_dn_string);
  466. }
  467. } else
  468. {
  469. slapi_log_error(SLAPI_LOG_REPL, NULL, "map_dn_values: this entry is not ours %s\n", original_dn_string);
  470. }
  471. } else
  472. {
  473. slapi_log_error(SLAPI_LOG_REPL, NULL, "map_dn_values: no remote entry found for %s\n", original_dn_string);
  474. }
  475. if (remote_entry)
  476. {
  477. slapi_entry_free(remote_entry);
  478. remote_entry = NULL;
  479. }
  480. }
  481. /* Extract the dn string and store in the new value */
  482. if (new_dn_string)
  483. {
  484. Slapi_Value *new_value = NULL;
  485. if (NULL == new_vs)
  486. {
  487. new_vs = slapi_valueset_new();
  488. }
  489. new_value = slapi_value_new_string_passin(new_dn_string);
  490. slapi_valueset_add_value(new_vs,new_value);
  491. slapi_value_free(&new_value);
  492. }
  493. /* If not then we skip it */
  494. i = slapi_valueset_next_value(original_values,i,&original_value);
  495. if (original_dn)
  496. {
  497. slapi_sdn_free(&original_dn);
  498. }
  499. }/* while */
  500. if (new_vs)
  501. {
  502. *mapped_values = new_vs;
  503. }
  504. /* Restore the keep raw entry flag. */
  505. windows_private_set_keep_raw_entry(prp->agmt, 0);
  506. }
  507. static void
  508. windows_dump_ruvs(Object *supl_ruv_obj, Object *cons_ruv_obj)
  509. {
  510. if (slapi_is_loglevel_set(SLAPI_LOG_REPL))
  511. {
  512. slapi_log_error(SLAPI_LOG_REPL, NULL, "acquire_replica, supplier RUV:\n");
  513. if (supl_ruv_obj) {
  514. RUV* sup = NULL;
  515. object_acquire(supl_ruv_obj);
  516. sup = (RUV*) object_get_data ( supl_ruv_obj );
  517. ruv_dump (sup, "supplier", NULL);
  518. object_release(supl_ruv_obj);
  519. } else
  520. {
  521. slapi_log_error(SLAPI_LOG_REPL, NULL, "acquire_replica, supplier RUV = null\n");
  522. }
  523. slapi_log_error(SLAPI_LOG_REPL, NULL, "acquire_replica, consumer RUV:\n");
  524. if (cons_ruv_obj)
  525. {
  526. RUV* con = NULL;
  527. object_acquire(cons_ruv_obj);
  528. con = (RUV*) object_get_data ( cons_ruv_obj );
  529. ruv_dump (con,"consumer", NULL);
  530. object_release( cons_ruv_obj );
  531. } else {
  532. slapi_log_error(SLAPI_LOG_REPL, NULL, "acquire_replica, consumer RUV = null\n");
  533. }
  534. }
  535. }
  536. /*
  537. * Acquire exclusive access to a replica. Send a start replication extended
  538. * operation to the replica. The response will contain a success code, and
  539. * optionally the replica's update vector if acquisition is successful.
  540. * This function returns one of the following:
  541. * ACQUIRE_SUCCESS - the replica was acquired, and we have exclusive update access
  542. * ACQUIRE_REPLICA_BUSY - another master was updating the replica
  543. * ACQUIRE_FATAL_ERROR - something bad happened, and it's not likely to improve
  544. * if we wait.
  545. * ACQUIRE_TRANSIENT_ERROR - something bad happened, but it's probably worth
  546. * another try after waiting a while.
  547. * If ACQUIRE_SUCCESS is returned, then ruv will point to the replica's update
  548. * vector. It's possible that the replica does something goofy and doesn't
  549. * return us an update vector, so be prepared for ruv to be NULL (but this is
  550. * an error).
  551. */
  552. int
  553. windows_acquire_replica(Private_Repl_Protocol *prp, RUV **ruv, int check_ruv)
  554. {
  555. int return_value = ACQUIRE_SUCCESS;
  556. ConnResult crc = 0;
  557. Repl_Connection *conn = NULL;
  558. Replica *replica = NULL;
  559. Object *supl_ruv_obj, *cons_ruv_obj = NULL;
  560. PRBool is_newer = PR_FALSE;
  561. RUV *r = NULL;
  562. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_acquire_replica\n", 0, 0, 0 );
  563. if (NULL == ruv)
  564. {
  565. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "NULL ruv\n");
  566. return_value = ACQUIRE_FATAL_ERROR;
  567. goto done;
  568. }
  569. PR_ASSERT(prp);
  570. if (prp->replica_acquired) /* we already acquire replica */
  571. {
  572. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  573. "%s: Remote replica already acquired\n",
  574. agmt_get_long_name(prp->agmt));
  575. return_value = ACQUIRE_FATAL_ERROR;
  576. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_acquire_replica\n", 0, 0, 0 );
  577. return ACQUIRE_SUCCESS;
  578. }
  579. if (NULL != *ruv)
  580. {
  581. ruv_destroy ( ruv );
  582. }
  583. object_acquire(prp->replica_object);
  584. replica = object_get_data(prp->replica_object);
  585. supl_ruv_obj = replica_get_ruv ( replica );
  586. cons_ruv_obj = agmt_get_consumer_ruv(prp->agmt);
  587. windows_dump_ruvs(supl_ruv_obj,cons_ruv_obj);
  588. is_newer = ruv_is_newer ( supl_ruv_obj, cons_ruv_obj );
  589. if (is_newer)
  590. {
  591. slapi_log_error(SLAPI_LOG_REPL, NULL, "acquire_replica, supplier RUV is newer\n");
  592. }
  593. /* Handle the pristine case */
  594. if (cons_ruv_obj == NULL)
  595. {
  596. *ruv = NULL;
  597. } else
  598. {
  599. r = (RUV*) object_get_data(cons_ruv_obj);
  600. *ruv = ruv_dup(r);
  601. }
  602. if ( supl_ruv_obj ) object_release ( supl_ruv_obj );
  603. if ( cons_ruv_obj ) object_release ( cons_ruv_obj );
  604. object_release (prp->replica_object);
  605. replica = NULL;
  606. /* Once we get here we have a valid ruv */
  607. if (is_newer == PR_FALSE && check_ruv) {
  608. prp->last_acquire_response_code = NSDS50_REPL_UPTODATE;
  609. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_acquire_replica - ACQUIRE_CONSUMER_WAS_UPTODATE\n", 0, 0, 0 );
  610. return ACQUIRE_CONSUMER_WAS_UPTODATE;
  611. }
  612. prp->last_acquire_response_code = NSDS50_REPL_REPLICA_NO_RESPONSE;
  613. /* Get the connection */
  614. conn = prp->conn;
  615. crc = windows_conn_connect(conn);
  616. if (CONN_OPERATION_FAILED == crc)
  617. {
  618. return_value = ACQUIRE_TRANSIENT_ERROR;
  619. }
  620. else if (CONN_SSL_NOT_ENABLED == crc)
  621. {
  622. return_value = ACQUIRE_FATAL_ERROR;
  623. }
  624. else
  625. {
  626. /* we don't want the timer to go off in the middle of an operation */
  627. windows_conn_cancel_linger(conn);
  628. /* Does the remote replica support the dirsync protocol?
  629. if it update the conn object */
  630. windows_conn_replica_supports_dirsync(conn);
  631. if (CONN_NOT_CONNECTED == crc || CONN_OPERATION_FAILED == crc)
  632. {
  633. /* We don't know anything about the remote replica. Try again later. */
  634. return_value = ACQUIRE_TRANSIENT_ERROR;
  635. }
  636. else
  637. {
  638. /* Good to go. Start the protocol. */
  639. CSN *current_csn = NULL;
  640. Slapi_DN *replarea_sdn;
  641. /* Obtain a current CSN */
  642. replarea_sdn = agmt_get_replarea(prp->agmt);
  643. current_csn = get_current_csn(replarea_sdn);
  644. if (NULL != current_csn)
  645. {
  646. return_value = ACQUIRE_SUCCESS;
  647. }
  648. else
  649. {
  650. /* Couldn't get a current CSN */
  651. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  652. "%s: Unable to obtain current CSN. "
  653. "Replication is aborting.\n",
  654. agmt_get_long_name(prp->agmt));
  655. return_value = ACQUIRE_FATAL_ERROR;
  656. }
  657. slapi_sdn_free(&replarea_sdn);
  658. csn_free(&current_csn);
  659. }
  660. }
  661. if (ACQUIRE_SUCCESS != return_value)
  662. {
  663. /* could not acquire the replica, so reinstate the linger timer, since this
  664. means we won't call release_replica, which also reinstates the timer */
  665. windows_conn_start_linger(conn);
  666. }
  667. else
  668. {
  669. /* replica successfully acquired */
  670. prp->replica_acquired = PR_TRUE;
  671. }
  672. done:
  673. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_acquire_replica\n", 0, 0, 0 );
  674. return return_value;
  675. }
  676. void
  677. windows_release_replica(Private_Repl_Protocol *prp)
  678. {
  679. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_release_replica\n", 0, 0, 0 );
  680. PR_ASSERT(NULL != prp);
  681. PR_ASSERT(NULL != prp->conn);
  682. if (!prp->replica_acquired)
  683. return;
  684. windows_conn_start_linger(prp->conn);
  685. prp->replica_acquired = PR_FALSE;
  686. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_release_replica\n", 0, 0, 0 );
  687. }
  688. static void
  689. to_little_endian_double_bytes(UChar *unicode_password, int32_t unicode_password_length)
  690. {
  691. int32_t i = 0;
  692. for (i = 0 ; i < unicode_password_length; i++)
  693. {
  694. UChar c = unicode_password[i];
  695. char *byte_ptr = (char*)&(unicode_password[i]);
  696. byte_ptr[0] = (char)(c & 0xff);
  697. byte_ptr[1] = (char)(c >> 8);
  698. }
  699. }
  700. /* this entry had a password, handle it seperately */
  701. /* http://support.microsoft.com/?kbid=269190 */
  702. static int
  703. send_password_modify(Slapi_DN *sdn, char *password, Private_Repl_Protocol *prp)
  704. {
  705. ConnResult pw_return = 0;
  706. int is_nt4 = windows_private_get_isnt4(prp->agmt);
  707. if (is_nt4)
  708. {
  709. /* NT4 just wants a plaintext password */
  710. Slapi_Mods smods = {0};
  711. slapi_mods_init (&smods, 0);
  712. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "UnicodePwd", password);
  713. pw_return = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(sdn), slapi_mods_get_ldapmods_byref(&smods), NULL, NULL );
  714. slapi_mods_done(&smods);
  715. } else
  716. {
  717. /* We will attempt to bind to AD with the new password first. We do
  718. * this to avoid playing a password change that originated from AD
  719. * back to AD. If we just played the password change back, then
  720. * both sides would be in sync, but AD would contain the new password
  721. * twice in it's password history, which undermines the password
  722. * history policies in AD. */
  723. if (windows_check_user_password(prp->conn, sdn, password)) {
  724. char *quoted_password = NULL;
  725. /* AD wants the password in quotes ! */
  726. quoted_password = PR_smprintf("\"%s\"",password);
  727. if (quoted_password)
  728. {
  729. LDAPMod *pw_mods[2];
  730. LDAPMod pw_mod;
  731. struct berval bv = {0};
  732. UChar *unicode_password = NULL;
  733. int32_t unicode_password_length = 0; /* Length in _characters_ */
  734. int32_t buffer_size = 0; /* Size in _characters_ */
  735. UErrorCode error = U_ZERO_ERROR;
  736. struct berval *bvals[2];
  737. /* Need to UNICODE encode the password here */
  738. /* It's one of those 'ask me first and I will tell you the buffer size' functions */
  739. u_strFromUTF8(NULL, 0, &unicode_password_length, quoted_password, strlen(quoted_password), &error);
  740. buffer_size = unicode_password_length;
  741. unicode_password = (UChar *)slapi_ch_malloc(unicode_password_length * sizeof(UChar));
  742. if (unicode_password) {
  743. error = U_ZERO_ERROR;
  744. u_strFromUTF8(unicode_password, buffer_size, &unicode_password_length, quoted_password, strlen(quoted_password), &error);
  745. /* As an extra special twist, we need to send the unicode in little-endian order for AD to be happy */
  746. to_little_endian_double_bytes(unicode_password, unicode_password_length);
  747. bv.bv_len = unicode_password_length * sizeof(UChar);
  748. bv.bv_val = (char*)unicode_password;
  749. bvals[0] = &bv;
  750. bvals[1] = NULL;
  751. pw_mod.mod_type = "UnicodePwd";
  752. pw_mod.mod_op = LDAP_MOD_REPLACE | LDAP_MOD_BVALUES;
  753. pw_mod.mod_bvalues = bvals;
  754. pw_mods[0] = &pw_mod;
  755. pw_mods[1] = NULL;
  756. pw_return = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(sdn), pw_mods, NULL, NULL );
  757. slapi_ch_free((void**)&unicode_password);
  758. }
  759. PR_smprintf_free(quoted_password);
  760. }
  761. } else {
  762. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  763. "%s: AD already has the current password for %s. "
  764. "Not sending password modify to AD.\n",
  765. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(sdn));
  766. }
  767. }
  768. return pw_return;
  769. }
  770. static int
  771. send_accountcontrol_modify(Slapi_DN *sdn, Private_Repl_Protocol *prp, int missing_entry)
  772. {
  773. ConnResult mod_return = 0;
  774. Slapi_Mods smods = {0};
  775. Slapi_Entry *remote_entry = NULL;
  776. int retval;
  777. unsigned long acctval = 0;
  778. char acctvalstr[32];
  779. /* have to first retrieve the existing entry - userAccountControl is
  780. a bit array, and we must preserve the existing values if any */
  781. /* Get the remote entry */
  782. retval = windows_get_remote_entry(prp, sdn, &remote_entry);
  783. if (0 == retval && remote_entry) {
  784. acctval = slapi_entry_attr_get_ulong(remote_entry, "userAccountControl");
  785. }
  786. slapi_entry_free(remote_entry);
  787. /* if we are adding a new entry, we need to set the entry to be
  788. enabled to allow AD login */
  789. if (missing_entry) {
  790. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  791. "%s: New Windows entry %s will be enabled.\n",
  792. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(sdn));
  793. acctval &= ~0x2; /* unset the disabled bit, if set */
  794. }
  795. /* set the account to be a normal account */
  796. acctval |= 0x0200; /* normal account == 512 */
  797. slapi_mods_init (&smods, 0);
  798. PR_snprintf(acctvalstr, sizeof(acctvalstr), "%lu", acctval);
  799. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "userAccountControl", acctvalstr);
  800. mod_return = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(sdn), slapi_mods_get_ldapmods_byref(&smods), NULL, NULL );
  801. slapi_mods_done(&smods);
  802. return mod_return;
  803. }
  804. static int
  805. windows_entry_has_attr_and_value(Slapi_Entry *e, const char *attrname, char *value)
  806. {
  807. int retval = 0;
  808. Slapi_Attr *attr = NULL;
  809. if (NULL == e || NULL == attrname)
  810. {
  811. return retval;
  812. }
  813. /* see if the entry has the specified attribute name */
  814. if (0 == slapi_entry_attr_find(e, attrname, &attr) && attr)
  815. {
  816. /* if value is not null, see if the attribute has that
  817. value */
  818. if (value)
  819. {
  820. Slapi_Value *v = NULL;
  821. int index = 0;
  822. for (index = slapi_attr_first_value(attr, &v);
  823. v && (index != -1);
  824. index = slapi_attr_next_value(attr, index, &v))
  825. {
  826. const char *s = slapi_value_get_string(v);
  827. if (NULL == s)
  828. {
  829. continue;
  830. }
  831. if (0 == strcasecmp(s, value))
  832. {
  833. retval = 1;
  834. break;
  835. }
  836. }
  837. }
  838. }
  839. return retval;
  840. }
  841. static void
  842. windows_is_local_entry_user_or_group(Slapi_Entry *e, int *is_user, int *is_group)
  843. {
  844. *is_user = windows_entry_has_attr_and_value(e,"objectclass","ntuser");
  845. *is_group = windows_entry_has_attr_and_value(e,"objectclass","ntgroup");
  846. }
  847. static void
  848. windows_is_remote_entry_user_or_group(Slapi_Entry *e, int *is_user, int *is_group)
  849. {
  850. *is_user = windows_entry_has_attr_and_value(e,"objectclass","person");
  851. *is_group = windows_entry_has_attr_and_value(e,"objectclass","group");
  852. }
  853. static int
  854. add_remote_entry_allowed(Slapi_Entry *e)
  855. {
  856. /* We say yes if the entry has the ntUserCreateNewAccount attribute set in the case of a user, or the ntGroupDeleteGroup
  857. * attribute set in the case of a group
  858. */
  859. /* Is this a user or a group ? */
  860. int is_user = 0;
  861. int is_group = 0;
  862. char *delete_attr = NULL;
  863. windows_is_local_entry_user_or_group(e,&is_user,&is_group);
  864. if (!is_user && !is_group)
  865. {
  866. /* Neither fish nor foul.. */
  867. return -1;
  868. }
  869. if (is_user && is_group)
  870. {
  871. /* Now that's just really strange... */
  872. return -1;
  873. }
  874. if (is_user)
  875. {
  876. delete_attr = "ntUserCreateNewAccount";
  877. } else
  878. {
  879. delete_attr = "ntGroupCreateNewGroup";
  880. }
  881. /* Now test if the attribute value is set */
  882. return windows_entry_has_attr_and_value(e,delete_attr,"true");
  883. }
  884. /* Tells us if we're allowed to add this (remote) entry locally */
  885. static int
  886. add_local_entry_allowed(Private_Repl_Protocol *prp, Slapi_Entry *e)
  887. {
  888. int is_user = 0;
  889. int is_group = 0;
  890. windows_is_remote_entry_user_or_group(e,&is_user,&is_group);
  891. if (is_user)
  892. {
  893. return windows_private_create_users(prp->agmt);
  894. }
  895. if (is_group)
  896. {
  897. return windows_private_create_groups(prp->agmt);
  898. }
  899. /* Default to 'no' */
  900. return 0;
  901. }
  902. static int
  903. delete_remote_entry_allowed(Slapi_Entry *e)
  904. {
  905. /* We say yes if the entry has the ntUserDeleteAccount attribute set in the case of a user, or the ntGroupDeleteGroup
  906. * attribute set in the case of a group
  907. */
  908. /* Is this a user or a group ? */
  909. int is_user = 0;
  910. int is_group = 0;
  911. char *delete_attr = NULL;
  912. windows_is_local_entry_user_or_group(e,&is_user,&is_group);
  913. if (!is_user && !is_group)
  914. {
  915. /* Neither fish nor foul.. */
  916. return 0;
  917. }
  918. if (is_user && is_group)
  919. {
  920. /* Now that's just really strange... */
  921. return 0;
  922. }
  923. if (is_user)
  924. {
  925. delete_attr = "ntUserDeleteAccount";
  926. } else
  927. {
  928. delete_attr = "ntGroupDeleteGroup";
  929. }
  930. /* Now test if the attribute value is set */
  931. return windows_entry_has_attr_and_value(e,delete_attr,"true");
  932. }
  933. static void
  934. windows_log_add_entry_remote(const Slapi_DN *local_dn,const Slapi_DN *remote_dn)
  935. {
  936. const char* local_dn_string = slapi_sdn_get_dn(local_dn);
  937. const char* remote_dn_string = slapi_sdn_get_dn(remote_dn);
  938. slapi_log_error(SLAPI_LOG_REPL, NULL, "Attempting to add entry %s to AD for local entry %s\n",remote_dn_string,local_dn_string);
  939. }
  940. /*
  941. * The entry may have been modified to make it "sync-able", so the modify operation should
  942. * actually trigger the addition of the entry to windows
  943. * check the list of mods to see if the sync objectclass/attributes were added to the entry
  944. * and if so if the current local entry still has them
  945. */
  946. static int
  947. sync_attrs_added(LDAPMod **original_mods, Slapi_Entry *local_entry) {
  948. int retval = 0;
  949. int ii = 0;
  950. char *useroc = "ntuser";
  951. char *groupoc = "ntgroup";
  952. size_t ulen = 6;
  953. size_t glen = 7;
  954. for (ii = 0; (retval == 0) && original_mods && original_mods[ii]; ++ii) {
  955. LDAPMod *mod = original_mods[ii];
  956. /* look for a mod/add or replace op with valid type and values */
  957. if (!(SLAPI_IS_MOD_ADD(mod->mod_op) || SLAPI_IS_MOD_REPLACE(mod->mod_op)) ||
  958. !mod->mod_type || !mod->mod_bvalues || !mod->mod_bvalues[0]) {
  959. continue; /* skip it */
  960. }
  961. /* if it has an objectclass mod, see if ntuser or ntgroup is one of them */
  962. if (!strcasecmp(mod->mod_type, "objectclass")) {
  963. int jj = 0;
  964. for (jj = 0; (retval == 0) && mod->mod_bvalues[jj]; ++jj) {
  965. struct berval *bv = mod->mod_bvalues[jj];
  966. if (((bv->bv_len == ulen) && !PL_strncasecmp(useroc, bv->bv_val, ulen)) ||
  967. ((bv->bv_len == glen) && !PL_strncasecmp(groupoc, bv->bv_val, glen))) {
  968. retval = 1; /* has magic objclass value */
  969. }
  970. }
  971. }
  972. }
  973. /* if the modify op had the right values, see if they are still present in
  974. the local entry */
  975. if (retval == 1) {
  976. retval = add_remote_entry_allowed(local_entry); /* check local entry */
  977. if (retval < 0) {
  978. retval = 0;
  979. }
  980. }
  981. return retval;
  982. }
  983. static ConnResult
  984. process_replay_add(Private_Repl_Protocol *prp, Slapi_Entry *add_entry, Slapi_Entry *local_entry, Slapi_DN *local_dn, Slapi_DN *remote_dn, int is_user, int missing_entry, char **password)
  985. {
  986. int remote_add_allowed = add_remote_entry_allowed(local_entry);
  987. ConnResult return_value = 0;
  988. int rc = 0;
  989. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  990. "%s: process_replay_add: dn=\"%s\" (%s,%s)\n", agmt_get_long_name(prp->agmt),
  991. slapi_sdn_get_dn(remote_dn), missing_entry ? "not present" : "already present",
  992. remote_add_allowed ? "add allowed" : "add not allowed");
  993. if (missing_entry)
  994. {
  995. /* If DN is a GUID, we need to attempt to reanimate the tombstone */
  996. if (is_guid_dn(remote_dn)) {
  997. int tstone_exists = 0;
  998. int reanimate_rc = -1;
  999. char *new_dn_string = NULL;
  1000. char *cn_string = NULL;
  1001. Slapi_DN *tombstone_dn = NULL;
  1002. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1003. "%s: process_replay_add: dn=\"%s\" appears to have been"
  1004. " deleted on remote side. Searching for tombstone.\n",
  1005. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn));
  1006. /* Map local entry to tombstone DN and verify that it exists on
  1007. * AD side */
  1008. map_windows_tombstone_dn(local_entry, &tombstone_dn, prp, &tstone_exists);
  1009. /* We can't use a GUID DN, so rewrite to the new mapped DN. */
  1010. cn_string = slapi_entry_attr_get_charptr(local_entry,"cn");
  1011. if (!cn_string) {
  1012. cn_string = slapi_entry_attr_get_charptr(local_entry,"ntuserdomainid");
  1013. }
  1014. if (cn_string) {
  1015. char *container_str = NULL;
  1016. const char *suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
  1017. container_str = extract_container(slapi_entry_get_sdn_const(local_entry),
  1018. windows_private_get_directory_subtree(prp->agmt));
  1019. new_dn_string = PR_smprintf("cn=%s,%s%s", cn_string, container_str, suffix);
  1020. if (new_dn_string) {
  1021. /* If the tombstone exists, reanimate it. If the tombstone
  1022. * does not exist, we'll create a new entry in AD, which
  1023. * will end up getting a new GUID generated by AD. */
  1024. if (tstone_exists) {
  1025. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1026. "%s: process_replay_add: Reanimating tombstone (dn=\"%s\") to"
  1027. " normal entry (dn=\"%s\").\n", agmt_get_long_name(prp->agmt),
  1028. slapi_sdn_get_dn(tombstone_dn), new_dn_string);
  1029. reanimate_rc = windows_reanimate_tombstone(prp, tombstone_dn, (const char *)new_dn_string);
  1030. if (reanimate_rc != 0) {
  1031. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1032. "%s: process_replay_add: Reanimation of tombstone"
  1033. " (dn=\"%s\") failed. A new entry (dn=\"%s\")"
  1034. " will be added instead.\n", agmt_get_long_name(prp->agmt),
  1035. slapi_sdn_get_dn(tombstone_dn), new_dn_string);
  1036. }
  1037. }
  1038. /* Clear out the old GUID DN and use the new one. We hand off the memory
  1039. * for new_dn_string into the remote_dn. */
  1040. slapi_sdn_done(remote_dn);
  1041. slapi_sdn_set_dn_passin(remote_dn, new_dn_string);
  1042. }
  1043. slapi_ch_free_string(&cn_string);
  1044. slapi_ch_free_string(&container_str);
  1045. }
  1046. if (tombstone_dn) {
  1047. slapi_sdn_free(&tombstone_dn);
  1048. }
  1049. if (reanimate_rc == 0) {
  1050. /* We reanimated a tombstone, so an add won't work. We
  1051. * fallback to doing a modify of the newly reanimated
  1052. * entry. */
  1053. goto modify_fallback;
  1054. }
  1055. }
  1056. if (remote_add_allowed) {
  1057. LDAPMod **entryattrs = NULL;
  1058. Slapi_Entry *mapped_entry = NULL;
  1059. /* First map the entry */
  1060. rc = windows_create_remote_entry(prp,add_entry, remote_dn, &mapped_entry, password);
  1061. /* Convert entry to mods */
  1062. if (0 == rc && mapped_entry)
  1063. {
  1064. (void)slapi_entry2mods (mapped_entry , NULL /* &entrydn : We don't need it */, &entryattrs);
  1065. slapi_entry_free(mapped_entry);
  1066. mapped_entry = NULL;
  1067. if (NULL == entryattrs)
  1068. {
  1069. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  1070. "%s: windows_replay_add: Cannot convert entry to LDAPMods.\n",
  1071. agmt_get_long_name(prp->agmt));
  1072. return_value = CONN_LOCAL_ERROR;
  1073. }
  1074. else
  1075. {
  1076. windows_log_add_entry_remote(local_dn, remote_dn);
  1077. return_value = windows_conn_send_add(prp->conn, slapi_sdn_get_dn(remote_dn),
  1078. entryattrs, NULL, NULL);
  1079. /* It's possible that the entry already exists in AD, in which
  1080. * case we fall back to modify it */
  1081. /* NGK - This fallback doesn't seem to happen, at least not at this point
  1082. * in the code. The only chance to fallback to doing a modify is if
  1083. * missing_entry is set to 0 at the top of this function. */
  1084. if (return_value)
  1085. {
  1086. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  1087. "%s: windows_replay_add: Cannot replay add operation.\n",
  1088. agmt_get_long_name(prp->agmt));
  1089. }
  1090. ldap_mods_free(entryattrs, 1);
  1091. entryattrs = NULL;
  1092. }
  1093. } else
  1094. {
  1095. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  1096. "%s: process_replay_add: failed to create mapped entry dn=\"%s\"\n",
  1097. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn));
  1098. }
  1099. }
  1100. } else
  1101. {
  1102. Slapi_Entry *remote_entry;
  1103. modify_fallback:
  1104. remote_entry = NULL;
  1105. /* Fetch the remote entry */
  1106. rc = windows_get_remote_entry(prp, remote_dn,&remote_entry);
  1107. if (0 == rc && remote_entry) {
  1108. return_value = windows_update_remote_entry(prp,remote_entry,local_entry);
  1109. }
  1110. if (remote_entry)
  1111. {
  1112. slapi_entry_free(remote_entry);
  1113. }
  1114. }
  1115. return return_value;
  1116. }
  1117. /*
  1118. * If the local entry is a user, this function is called for moving a node.
  1119. * This is because the leaf RDN of the user on AD corresponds to the value of
  1120. * ntUniqueId in the entry on DS.
  1121. *
  1122. * If the local entry is a group, it is called both for moving and renaming.
  1123. * Group does not have the mapping. The leaf RDNs are shared between AD
  1124. * and DS.
  1125. */
  1126. static ConnResult
  1127. process_replay_rename(Private_Repl_Protocol *prp,
  1128. Slapi_Entry *local_newentry,
  1129. Slapi_DN *local_origsdn,
  1130. const char *newrdn,
  1131. const char *newparent,
  1132. int deleteoldrdn,
  1133. int is_user,
  1134. int is_group)
  1135. {
  1136. ConnResult rval = CONN_OPERATION_FAILED;
  1137. char *newsuperior = NULL;
  1138. const Repl_Agmt *winrepl_agmt;
  1139. const char *remote_subtree = NULL; /* Normalized subtree of the remote entry */
  1140. const char *local_subtree = NULL; /* Normalized subtree of the local entry */
  1141. char *norm_newparent = NULL;
  1142. char *p = NULL;
  1143. char *remote_rdn_val = NULL;
  1144. char *remote_rdn = NULL;
  1145. char *remote_dn = NULL;
  1146. char *local_pndn = NULL;
  1147. if (NULL == newparent || NULL == local_origsdn || NULL == local_newentry) {
  1148. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  1149. "process_replay_rename: %s is empty\n",
  1150. NULL==newparent?"newparent":NULL==local_origsdn?"local sdn":
  1151. NULL==local_newentry?"local entry":"unknown");
  1152. goto bail;
  1153. }
  1154. if (0 == is_user && 0 == is_group) {
  1155. goto bail; /* nothing to do */
  1156. }
  1157. /* Generate newsuperior for AD */
  1158. winrepl_agmt = prp->agmt;
  1159. remote_subtree =
  1160. slapi_sdn_get_ndn(windows_private_get_windows_subtree(winrepl_agmt));
  1161. local_subtree =
  1162. slapi_sdn_get_ndn(windows_private_get_directory_subtree(winrepl_agmt));
  1163. if (NULL == remote_subtree || NULL == local_subtree ||
  1164. '\0' == *remote_subtree || '\0' == *local_subtree) {
  1165. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  1166. "process_replay_rename: local subtree \"%s\" or "
  1167. "remote subtree \"%s\" is empty\n",
  1168. local_subtree?local_subtree:"empty",
  1169. remote_subtree?remote_subtree:"empty");
  1170. goto bail;
  1171. }
  1172. norm_newparent = slapi_ch_strdup(newparent);
  1173. slapi_dn_normalize_case(norm_newparent);
  1174. p = strstr(norm_newparent, local_subtree);
  1175. if (NULL == p) {
  1176. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  1177. "process_replay_rename: new superior \"%s\" is not "
  1178. "in the local subtree \"%s\"\n",
  1179. norm_newparent, local_subtree);
  1180. goto bail; /* not in the subtree */
  1181. }
  1182. *p = '\0';
  1183. if (p == norm_newparent) {
  1184. newsuperior = PR_smprintf("%s", remote_subtree);
  1185. } else {
  1186. newsuperior = PR_smprintf("%s%s", norm_newparent, remote_subtree);
  1187. }
  1188. if (is_user) {
  1189. /* Newrdn remains the same when this function is called,
  1190. * as RDN on AD is CN type. If CN in RDN is modified remotely,
  1191. * is taken care in modify not in modrdn locally. */
  1192. remote_rdn_val = slapi_entry_attr_get_charptr(local_newentry, "cn");
  1193. if (NULL == remote_rdn_val) {
  1194. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  1195. "process_replay_rename: local entry \"%s\" has no "
  1196. "ntUserDomainId\n",
  1197. slapi_entry_get_dn_const(local_newentry));
  1198. goto bail;
  1199. }
  1200. remote_rdn = PR_smprintf("cn=%s", remote_rdn_val);
  1201. } else if (is_group) {
  1202. Slapi_RDN rdn = {0};
  1203. const char *dn = slapi_sdn_get_dn(local_origsdn);
  1204. slapi_rdn_set_dn(&rdn, dn);
  1205. remote_rdn = slapi_ch_strdup(slapi_rdn_get_rdn(&rdn));
  1206. slapi_rdn_done(&rdn);
  1207. }
  1208. /* local parent normalized dn */
  1209. local_pndn = /* strdup'ed */
  1210. slapi_dn_parent((const char *)slapi_sdn_get_ndn(local_origsdn));
  1211. p = strstr(local_pndn, local_subtree);
  1212. if (NULL == p) {
  1213. /* Original entry is not in the subtree.
  1214. * To add the entry after returning from this function,
  1215. * we set LDAP_NO_SUCH_OBJECT to the ldap error */
  1216. windows_conn_set_error(prp->conn, LDAP_NO_SUCH_OBJECT);
  1217. goto bail;
  1218. }
  1219. *p = '\0';
  1220. /* generate a remote dn */
  1221. remote_dn = PR_smprintf("%s,%s%s", remote_rdn, local_pndn, remote_subtree);
  1222. if (is_user) {
  1223. rval = windows_conn_send_rename(prp->conn, remote_dn,
  1224. remote_rdn, (const char *)newsuperior,
  1225. deleteoldrdn, NULL, NULL /* returned controls */);
  1226. } else {
  1227. rval = windows_conn_send_rename(prp->conn, remote_dn,
  1228. newrdn, (const char *)newsuperior,
  1229. deleteoldrdn, NULL, NULL /* returned controls */);
  1230. }
  1231. bail:
  1232. slapi_ch_free_string(&norm_newparent);
  1233. slapi_ch_free_string(&remote_rdn_val);
  1234. slapi_ch_free_string(&remote_rdn);
  1235. slapi_ch_free_string(&remote_dn);
  1236. slapi_ch_free_string(&local_pndn);
  1237. slapi_ch_free_string(&newsuperior);
  1238. return rval;
  1239. }
  1240. /*
  1241. * Given a changelog entry, construct the appropriate LDAP operations to sync
  1242. * the operation to AD.
  1243. */
  1244. ConnResult
  1245. windows_replay_update(Private_Repl_Protocol *prp, slapi_operation_parameters *op)
  1246. {
  1247. ConnResult return_value = 0;
  1248. int rc = 0;
  1249. char *password = NULL;
  1250. int is_ours = 0;
  1251. int is_ours_force = 0; /* force to operate for RENAME/MODRDN */
  1252. int is_user = 0;
  1253. int is_group = 0;
  1254. Slapi_DN *remote_dn = NULL;
  1255. Slapi_DN *local_dn = NULL;
  1256. Slapi_Entry *local_entry = NULL;
  1257. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_replay_update\n", 0, 0, 0 );
  1258. local_dn = slapi_sdn_new_dn_byref( op->target_address.dn );
  1259. /* Since we have the target uniqueid in the op structure, let's
  1260. * fetch the local entry here using it. We do not want to search
  1261. * across tombstone entries unless we are dealing with a delete
  1262. * operation here since searching across tombstones can be very
  1263. * inefficient as the tombstones build up.
  1264. */
  1265. if (op->operation_type != SLAPI_OPERATION_DELETE) {
  1266. rc = windows_get_local_entry_by_uniqueid(prp, op->target_address.uniqueid, &local_entry, 0);
  1267. } else {
  1268. rc = windows_get_local_tombstone_by_uniqueid(prp, op->target_address.uniqueid, &local_entry);
  1269. }
  1270. if (rc)
  1271. {
  1272. if (SLAPI_OPERATION_MODRDN == op->operation_type) {
  1273. /* Local entry was moved out of the subtree */
  1274. /* We need to remove the entry on AD. */
  1275. rc = windows_get_local_entry_by_uniqueid(prp,
  1276. op->target_address.uniqueid, &local_entry, 1 /* is_global */);
  1277. if (rc) {
  1278. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  1279. "%s: windows_replay_update: failed to fetch local entry "
  1280. "for %s operation dn=\"%s\"\n",
  1281. agmt_get_long_name(prp->agmt),
  1282. op2string(op->operation_type), op->target_address.dn);
  1283. goto error;
  1284. }
  1285. op->operation_type = SLAPI_OPERATION_DELETE;
  1286. is_ours_force = 1;
  1287. } else {
  1288. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  1289. "%s: windows_replay_update: failed to fetch local entry for %s operation dn=\"%s\"\n",
  1290. agmt_get_long_name(prp->agmt),
  1291. op2string(op->operation_type), op->target_address.dn);
  1292. goto error;
  1293. }
  1294. }
  1295. if (is_ours_force) {
  1296. is_ours = is_ours_force;
  1297. } else {
  1298. is_ours = is_subject_of_agreement_local(local_entry, prp->agmt);
  1299. }
  1300. windows_is_local_entry_user_or_group(local_entry,&is_user,&is_group);
  1301. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1302. "%s: windows_replay_update: Looking at %s operation local dn=\"%s\" (%s,%s,%s)\n",
  1303. agmt_get_long_name(prp->agmt),
  1304. op2string(op->operation_type), op->target_address.dn, is_ours ? "ours" : "not ours",
  1305. is_user ? "user" : "not user", is_group ? "group" : "not group");
  1306. if (is_ours && (is_user || is_group) ) {
  1307. int missing_entry = 0;
  1308. /* Make the entry's DN */
  1309. rc = map_entry_dn_outbound(local_entry,&remote_dn,prp,&missing_entry, 1);
  1310. if (rc || NULL == remote_dn)
  1311. {
  1312. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  1313. "%s: windows_replay_update: failed map dn for %s operation dn=\"%s\""
  1314. "rc=%d remote_dn = [%s]\n",
  1315. agmt_get_long_name(prp->agmt),
  1316. op2string(op->operation_type), op->target_address.dn,
  1317. rc, remote_dn ? slapi_sdn_get_dn(remote_dn) : "(null)");
  1318. goto error;
  1319. }
  1320. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1321. "%s: windows_replay_update: Processing %s operation local dn=\"%s\" remote dn=\"%s\"\n",
  1322. agmt_get_long_name(prp->agmt),
  1323. op2string(op->operation_type), op->target_address.dn, slapi_sdn_get_dn(remote_dn));
  1324. switch (op->operation_type) {
  1325. case SLAPI_OPERATION_ADD:
  1326. return_value = process_replay_add(prp,op->p.p_add.target_entry,local_entry,local_dn,remote_dn,is_user,missing_entry,&password);
  1327. break;
  1328. case SLAPI_OPERATION_MODIFY:
  1329. {
  1330. LDAPMod **mapped_mods = NULL;
  1331. char *newrdn = NULL;
  1332. /*
  1333. * If the magic objectclass and attributes have been added to the entry
  1334. * to make the entry sync-able, add the entry first, then apply the other
  1335. * mods
  1336. */
  1337. if (sync_attrs_added(op->p.p_modify.modify_mods, local_entry)) {
  1338. Slapi_Entry *ad_entry = NULL;
  1339. return_value = process_replay_add(prp,local_entry,local_entry,local_dn,remote_dn,is_user,missing_entry,&password);
  1340. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1341. "%s: windows_replay_update: "
  1342. "The modify operation added the sync objectclass and attribute, so "
  1343. "the entry was added to windows - result [%d]\n",
  1344. agmt_get_long_name(prp->agmt), return_value);
  1345. if (return_value) {
  1346. break; /* error adding entry - cannot continue */
  1347. }
  1348. /* the modify op needs the new remote entry, so retrieve it */
  1349. windows_get_remote_entry(prp, remote_dn, &ad_entry);
  1350. slapi_entry_free(ad_entry); /* getting sets windows_private_get_raw_entry */
  1351. }
  1352. windows_map_mods_for_replay(prp,op->p.p_modify.modify_mods, &mapped_mods, is_user, &password);
  1353. if (is_user) {
  1354. winsync_plugin_call_pre_ad_mod_user_mods_cb(prp->agmt,
  1355. windows_private_get_raw_entry(prp->agmt),
  1356. local_dn,
  1357. local_entry,
  1358. op->p.p_modify.modify_mods,
  1359. remote_dn,
  1360. &mapped_mods);
  1361. } else if (is_group) {
  1362. winsync_plugin_call_pre_ad_mod_group_mods_cb(prp->agmt,
  1363. windows_private_get_raw_entry(prp->agmt),
  1364. local_dn,
  1365. local_entry,
  1366. op->p.p_modify.modify_mods,
  1367. remote_dn,
  1368. &mapped_mods);
  1369. }
  1370. /* Check if a naming attribute is being modified. */
  1371. if (windows_check_mods_for_rdn_change(prp, op->p.p_modify.modify_mods, local_entry, remote_dn, &newrdn)) {
  1372. /* Issue MODRDN */
  1373. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name, "%s: renaming remote entry \"%s\" with new RDN of \"%s\"\n",
  1374. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn), newrdn);
  1375. return_value = windows_conn_send_rename(prp->conn, slapi_sdn_get_dn(remote_dn),
  1376. newrdn, NULL, 1 /* delete old RDN */,
  1377. NULL, NULL /* returned controls */);
  1378. slapi_ch_free_string(&newrdn);
  1379. }
  1380. /* It's possible that the mapping process results in an empty mod list, in which case we don't bother with the replay */
  1381. if ( mapped_mods == NULL || *(mapped_mods)== NULL )
  1382. {
  1383. return_value = CONN_OPERATION_SUCCESS;
  1384. } else
  1385. {
  1386. if (slapi_is_loglevel_set(SLAPI_LOG_REPL))
  1387. {
  1388. int i = 0;
  1389. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,"dump mods for replay update:");
  1390. for(i=0;mapped_mods[i];i++)
  1391. {
  1392. slapi_mod_dump(mapped_mods[i],i);
  1393. }
  1394. }
  1395. return_value = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(remote_dn), mapped_mods, NULL, NULL /* returned controls */);
  1396. }
  1397. if (mapped_mods)
  1398. {
  1399. ldap_mods_free(mapped_mods,1);
  1400. mapped_mods = NULL;
  1401. }
  1402. }
  1403. break;
  1404. case SLAPI_OPERATION_DELETE:
  1405. if (delete_remote_entry_allowed(local_entry))
  1406. {
  1407. return_value = windows_conn_send_delete(prp->conn, slapi_sdn_get_dn(remote_dn), NULL, NULL /* returned controls */);
  1408. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1409. "%s: windows_replay_update: deleted remote entry, dn=\"%s\", result=%d\n",
  1410. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn), return_value);
  1411. } else
  1412. {
  1413. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1414. "%s: windows_replay_update: delete not allowed on remote entry, dn=\"%s\"\n",
  1415. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(remote_dn));
  1416. }
  1417. break;
  1418. case SLAPI_OPERATION_MODRDN:
  1419. /* only move case (newsuperior: ...) comse here since local leaf RDN is
  1420. * not identical to the remote leaf RDN. */
  1421. {
  1422. return_value = process_replay_rename(prp, local_entry, local_dn,
  1423. op->p.p_modrdn.modrdn_newrdn,
  1424. op->p.p_modrdn.modrdn_newsuperior_address.dn,
  1425. op->p.p_modrdn.modrdn_deloldrdn,
  1426. is_user, is_group);
  1427. if (CONN_OPERATION_FAILED == return_value) {
  1428. int operation = 0;
  1429. int error = 0;
  1430. conn_get_error(prp->conn, &operation, &error);
  1431. /* The remote entry is missing. Let's add the renamed entry. */
  1432. if (LDAP_NO_SUCH_OBJECT == error) {
  1433. return_value = process_replay_add(prp,
  1434. local_entry /* target_entry */,
  1435. local_entry, local_dn, remote_dn,
  1436. is_user, missing_entry, &password);
  1437. }
  1438. }
  1439. break;
  1440. }
  1441. default:
  1442. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name, "%s: replay_update: Unknown "
  1443. "operation type %lu found in changelog - skipping change.\n",
  1444. agmt_get_long_name(prp->agmt), op->operation_type);
  1445. }
  1446. if (password)
  1447. {
  1448. /* We need to have a non-GUID dn in send_password_modify in order to
  1449. * bind as the user to check if we need to send the password change.
  1450. * You are supposed to be able to bind using a GUID dn, but it doesn't
  1451. * seem to work over plain LDAP. */
  1452. if (is_guid_dn(remote_dn)) {
  1453. Slapi_DN *remote_dn_norm = NULL;
  1454. int norm_missing = 0;
  1455. map_entry_dn_outbound(local_entry,&remote_dn_norm,prp,&norm_missing, 0);
  1456. return_value = send_password_modify(remote_dn_norm, password, prp);
  1457. slapi_sdn_free(&remote_dn_norm);
  1458. } else {
  1459. return_value = send_password_modify(remote_dn, password, prp);
  1460. }
  1461. if (return_value)
  1462. {
  1463. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  1464. "%s: windows_replay_update: update password returned %d\n",
  1465. agmt_get_long_name(prp->agmt), return_value );
  1466. }
  1467. }
  1468. /* If we successfully added an entry, and then subsequently changed
  1469. * its password, THEN we need to change its status in AD in order
  1470. * that it can be used (otherwise the user is marked as disabled).
  1471. * To do this we set this attribute and value:
  1472. * userAccountControl: 512
  1473. * Or, if we added a new entry, we need to change the useraccountcontrol
  1474. * to make the new user enabled by default
  1475. */
  1476. if ((return_value == CONN_OPERATION_SUCCESS) && remote_dn && (password || missing_entry)) {
  1477. return_value = send_accountcontrol_modify(remote_dn, prp, missing_entry);
  1478. }
  1479. } else {
  1480. /* We ignore operations that target entries outside of our sync'ed subtree, or which are not Windows users or groups */
  1481. }
  1482. error:
  1483. if (local_entry)
  1484. {
  1485. slapi_entry_free(local_entry);
  1486. }
  1487. if (local_dn)
  1488. {
  1489. slapi_sdn_free (&local_dn);
  1490. }
  1491. if (remote_dn)
  1492. {
  1493. slapi_sdn_free(&remote_dn);
  1494. }
  1495. slapi_ch_free_string(&password);
  1496. return return_value;
  1497. }
  1498. static int
  1499. is_straight_mapped_attr(const char *type, int is_user /* or group */, int is_nt4)
  1500. {
  1501. int found = 0;
  1502. size_t offset = 0;
  1503. char *this_attr = NULL;
  1504. char **list = is_user ? (is_nt4 ? nt4_user_matching_attributes : windows_user_matching_attributes) : (is_nt4 ? nt4_group_matching_attributes : windows_group_matching_attributes);
  1505. /* Look for the type in the list of straight mapped attrs for the appropriate object type */
  1506. while ((this_attr = list[offset]))
  1507. {
  1508. if (0 == slapi_attr_type_cmp(this_attr, type, SLAPI_TYPE_CMP_SUBTYPE))
  1509. {
  1510. found = 1;
  1511. break;
  1512. }
  1513. offset++;
  1514. }
  1515. return found;
  1516. }
  1517. static int
  1518. is_single_valued_attr(const char *type)
  1519. {
  1520. int found = 0;
  1521. size_t offset = 0;
  1522. char *this_attr = NULL;
  1523. /* Look for the type in the list of single-valued AD attributes */
  1524. while ((this_attr = windows_single_valued_attributes[offset]))
  1525. {
  1526. if (0 == slapi_attr_type_cmp(this_attr, type, SLAPI_TYPE_CMP_SUBTYPE))
  1527. {
  1528. found = 1;
  1529. break;
  1530. }
  1531. offset++;
  1532. }
  1533. return found;
  1534. }
  1535. static void
  1536. windows_map_attr_name(const char *original_type , int to_windows, int is_user, int is_create, char **mapped_type, int *map_dn)
  1537. {
  1538. char *new_type = NULL;
  1539. windows_attribute_map *our_map = is_user ? user_attribute_map : group_attribute_map;
  1540. windows_attribute_map *this_map = NULL;
  1541. size_t offset = 0;
  1542. *mapped_type = NULL;
  1543. /* Iterate over the map entries looking for the type we have */
  1544. while((this_map = &(our_map[offset])))
  1545. {
  1546. char *their_name = to_windows ? this_map->windows_attribute_name : this_map->ldap_attribute_name;
  1547. char *our_name = to_windows ? this_map->ldap_attribute_name : this_map->windows_attribute_name;
  1548. if (NULL == their_name)
  1549. {
  1550. /* End of the list */
  1551. break;
  1552. }
  1553. if (0 == slapi_attr_type_cmp(original_type, our_name, SLAPI_TYPE_CMP_SUBTYPE))
  1554. {
  1555. if (!is_create && (this_map->create_type == createonly))
  1556. {
  1557. /* Skip create-only entries if we're not creating */
  1558. } else
  1559. {
  1560. if ( (this_map->map_type == towindowsonly && to_windows) || (this_map->map_type == fromwindowsonly && !to_windows)
  1561. || (this_map->map_type == bidirectional) )
  1562. {
  1563. new_type = slapi_ch_strdup(their_name);
  1564. *map_dn = (this_map->attr_type == dnmap);
  1565. break;
  1566. }
  1567. }
  1568. }
  1569. offset++;
  1570. }
  1571. if (new_type)
  1572. {
  1573. *mapped_type = new_type;
  1574. }
  1575. }
  1576. /*
  1577. * Make a new entry suitable for the sync destination (indicated by the to_windows argument).
  1578. * Returns the new entry ready to be passed to an LDAP ADD operation, either remote or local.
  1579. * Also returns the plaintext value of any password contained in the original entry (only for the
  1580. * to_windows direction). This is because passwords must be added to entries after they are added in AD.
  1581. * Caller must free the new entry and any password returned.
  1582. */
  1583. static int
  1584. windows_create_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *original_entry, Slapi_DN *remote_sdn, Slapi_Entry **remote_entry, char** password)
  1585. {
  1586. int retval = 0;
  1587. char *entry_string = NULL;
  1588. Slapi_Entry *new_entry = NULL;
  1589. int rc = 0;
  1590. int is_user = 0;
  1591. int is_group = 0;
  1592. Slapi_Attr *attr = NULL;
  1593. char *username = NULL;
  1594. const char *dn_string = NULL;
  1595. char *fqusername = NULL;
  1596. const char *domain_name = windows_private_get_windows_domain(prp->agmt);
  1597. int is_nt4 = windows_private_get_isnt4(prp->agmt);
  1598. char *remote_user_entry_template =
  1599. "dn: %s\n"
  1600. "objectclass:top\n"
  1601. "objectclass:person\n"
  1602. "objectclass:organizationalperson\n"
  1603. "objectclass:user\n"
  1604. "userPrincipalName:%s\n";
  1605. char *remote_group_entry_template =
  1606. "dn: %s\n"
  1607. "objectclass:top\n"
  1608. "objectclass:group\n";
  1609. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_create_remote_entry\n", 0, 0, 0 );
  1610. windows_is_local_entry_user_or_group(original_entry,&is_user,&is_group);
  1611. /* Create a new entry */
  1612. /* Give it its DN and samaccountname */
  1613. username = extract_ntuserdomainid_from_entry(original_entry);
  1614. if (NULL == username)
  1615. {
  1616. goto error;
  1617. }
  1618. fqusername = PR_smprintf("%s@%s",username,domain_name);
  1619. dn_string = slapi_sdn_get_dn(remote_sdn);
  1620. if (is_user)
  1621. {
  1622. entry_string = slapi_ch_smprintf(remote_user_entry_template, dn_string, fqusername);
  1623. } else
  1624. {
  1625. entry_string = slapi_ch_smprintf(remote_group_entry_template, dn_string);
  1626. }
  1627. PR_smprintf_free(fqusername);
  1628. if (NULL == entry_string)
  1629. {
  1630. goto error;
  1631. }
  1632. new_entry = slapi_str2entry(entry_string, 0);
  1633. slapi_ch_free_string(&entry_string);
  1634. if (NULL == new_entry)
  1635. {
  1636. goto error;
  1637. }
  1638. /* Map the appropriate attributes sourced from the remote entry */
  1639. /* Iterate over the local entry's attributes */
  1640. for (rc = slapi_entry_first_attr(original_entry, &attr); rc == 0;
  1641. rc = slapi_entry_next_attr(original_entry, attr, &attr))
  1642. {
  1643. char *type = NULL;
  1644. Slapi_ValueSet *vs = NULL;
  1645. int mapdn = 0;
  1646. slapi_attr_get_type( attr, &type );
  1647. slapi_attr_get_valueset(attr,&vs);
  1648. if ( is_straight_mapped_attr(type,is_user,is_nt4) )
  1649. {
  1650. /* If this attribute is single-valued in AD,
  1651. * we only want to send the first value. */
  1652. if (is_single_valued_attr(type))
  1653. {
  1654. if (slapi_valueset_count(vs) > 1) {
  1655. int i = 0;
  1656. Slapi_Value *value = NULL;
  1657. Slapi_Value *new_value = NULL;
  1658. i = slapi_valueset_first_value(vs,&value);
  1659. if (i >= 0) {
  1660. /* Dup the first value, trash the valueset, then copy in the dup'd value. */
  1661. new_value = slapi_value_dup(value);
  1662. slapi_valueset_done(vs);
  1663. /* The below hands off the memory to the valueset */
  1664. slapi_valueset_add_value_ext(vs, new_value, SLAPI_VALUE_FLAG_PASSIN);
  1665. }
  1666. }
  1667. }
  1668. /* The initials attribute is a special case. AD has a constraint
  1669. * that limits the value length. If we're sending a change to
  1670. * the initials attribute to AD, we trim if neccessary.
  1671. */
  1672. if (0 == slapi_attr_type_cmp(type, "initials", SLAPI_TYPE_CMP_SUBTYPE)) {
  1673. int i = 0;
  1674. const char *initials_value = NULL;
  1675. Slapi_Value *value = NULL;
  1676. i = slapi_valueset_first_value(vs,&value);
  1677. while (i >= 0) {
  1678. initials_value = slapi_value_get_string(value);
  1679. /* If > AD_INITIALS_LENGTH, trim the value */
  1680. if (strlen(initials_value) > AD_INITIALS_LENGTH) {
  1681. char *new_initials = PL_strndup(initials_value, AD_INITIALS_LENGTH);
  1682. /* the below hands off memory */
  1683. slapi_value_set_string_passin(value, new_initials);
  1684. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1685. "%s: windows_create_remote_entry: "
  1686. "Trimming initials attribute to %d characters.\n",
  1687. agmt_get_long_name(prp->agmt), AD_INITIALS_LENGTH);
  1688. }
  1689. i = slapi_valueset_next_value(vs, i, &value);
  1690. }
  1691. }
  1692. /* copy over the attr values */
  1693. slapi_entry_add_valueset(new_entry,type,vs);
  1694. } else
  1695. {
  1696. char *new_type = NULL;
  1697. windows_map_attr_name(type , 1 /* to windows */, is_user, 1 /* create */, &new_type, &mapdn);
  1698. if (new_type)
  1699. {
  1700. if (mapdn)
  1701. {
  1702. Slapi_ValueSet *mapped_values = NULL;
  1703. map_dn_values(prp,vs,&mapped_values, 1 /* to windows */,0);
  1704. if (mapped_values)
  1705. {
  1706. slapi_entry_add_valueset(new_entry,new_type,mapped_values);
  1707. slapi_valueset_free(mapped_values);
  1708. mapped_values = NULL;
  1709. }
  1710. } else
  1711. {
  1712. Slapi_Attr *new_attr = NULL;
  1713. /* AD treats cn and streetAddress as a single-valued attributes, while we define
  1714. * them as multi-valued attribute as it's defined in rfc 4519. We only
  1715. * sync the first value to AD to avoid a constraint violation.
  1716. */
  1717. if ((0 == slapi_attr_type_cmp(new_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE)) ||
  1718. (0 == slapi_attr_type_cmp(new_type, "cn", SLAPI_TYPE_CMP_SUBTYPE))) {
  1719. if (slapi_valueset_count(vs) > 1) {
  1720. int i = 0;
  1721. Slapi_Value *value = NULL;
  1722. Slapi_Value *new_value = NULL;
  1723. i = slapi_valueset_first_value(vs,&value);
  1724. if (i >= 0) {
  1725. /* Dup the first value, trash the valueset, then copy
  1726. * in the dup'd value. */
  1727. new_value = slapi_value_dup(value);
  1728. slapi_valueset_done(vs);
  1729. /* The below hands off the memory to the valueset */
  1730. slapi_valueset_add_value_ext(vs, new_value, SLAPI_VALUE_FLAG_PASSIN);
  1731. }
  1732. }
  1733. }
  1734. slapi_entry_add_valueset(new_entry,type,vs);
  1735. /* Reset the type to new_type here. This is needed since
  1736. * slapi_entry_add_valueset will create the Slapi_Attrs using
  1737. * the schema definition, which can reset the type to something
  1738. * other than the type you pass into it. To be safe, we just
  1739. * create the attributes with the old type, then reset them. */
  1740. if (slapi_entry_attr_find(new_entry, type, &new_attr) == 0) {
  1741. slapi_attr_set_type(new_attr, new_type);
  1742. }
  1743. }
  1744. slapi_ch_free_string(&new_type);
  1745. }
  1746. /* password mods are treated specially */
  1747. if (0 == slapi_attr_type_cmp(type, PSEUDO_ATTR_UNHASHEDUSERPASSWORD, SLAPI_TYPE_CMP_SUBTYPE) )
  1748. {
  1749. const char *password_value = NULL;
  1750. Slapi_Value *value = NULL;
  1751. slapi_valueset_first_value(vs,&value);
  1752. password_value = slapi_value_get_string(value);
  1753. /* We need to check if the first character of password_value is an
  1754. * opening brace since strstr will simply return it's first argument
  1755. * if it is an empty string. */
  1756. if (password_value && (*password_value == '{')) {
  1757. if (strchr( password_value, '}' )) {
  1758. /* A storage scheme is present. Check if it's the
  1759. * clear storage scheme. */
  1760. if ((strlen(password_value) >= PASSWD_CLEAR_PREFIX_LEN + 1) &&
  1761. (strncasecmp(password_value, PASSWD_CLEAR_PREFIX, PASSWD_CLEAR_PREFIX_LEN) == 0)) {
  1762. /* This password is in clear text. Strip off the clear prefix
  1763. * and sync it. */
  1764. *password = slapi_ch_strdup(password_value + PASSWD_CLEAR_PREFIX_LEN);
  1765. } else {
  1766. /* This password is stored in a non-cleartext format.
  1767. * We can only sync cleartext passwords. */
  1768. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1769. "%s: windows_create_remote_entry: "
  1770. "Password is already hashed. Not syncing.\n",
  1771. agmt_get_long_name(prp->agmt));
  1772. }
  1773. } else {
  1774. /* This password doesn't have a storage prefix but
  1775. * just happens to start with the '{' character. We'll
  1776. * assume that it's just a cleartext password without
  1777. * the proper storage prefix. */
  1778. *password = slapi_ch_strdup(password_value);
  1779. }
  1780. } else {
  1781. /* This password has no storage prefix, or the password is empty */
  1782. *password = slapi_ch_strdup(password_value);
  1783. }
  1784. }
  1785. }
  1786. if (vs)
  1787. {
  1788. slapi_valueset_free(vs);
  1789. vs = NULL;
  1790. }
  1791. }
  1792. /* NT4 must have the groupType attribute set for groups. If it is not present, we will
  1793. * add it here with a value of 2 (global group).
  1794. */
  1795. if (is_nt4 && is_group)
  1796. {
  1797. Slapi_Attr *ap = NULL;
  1798. if(slapi_entry_attr_find(new_entry, "groupType", &ap))
  1799. {
  1800. /* groupType attribute wasn't found, so we'll add it */
  1801. slapi_entry_attr_set_int(new_entry, "groupType", 2 /* global group */);
  1802. }
  1803. }
  1804. if (remote_entry)
  1805. {
  1806. *remote_entry = new_entry;
  1807. }
  1808. error:
  1809. if (username)
  1810. {
  1811. slapi_ch_free_string(&username);
  1812. }
  1813. if (new_entry)
  1814. {
  1815. windows_dump_entry("Created new remote entry:\n",new_entry);
  1816. }
  1817. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_create_remote_entry: %d\n", retval, 0, 0 );
  1818. return retval;
  1819. }
  1820. #ifdef FOR_DEBUGGING
  1821. /* the entry has already been translated, so be sure to search for ntuserid
  1822. and not samaccountname or anything else. */
  1823. static Slapi_Entry*
  1824. windows_entry_already_exists(Slapi_Entry *e){
  1825. int rc = 0;
  1826. Slapi_DN *sdn = NULL;
  1827. Slapi_Entry *entry = NULL;
  1828. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_entry_already_exists\n", 0, 0, 0 );
  1829. sdn = slapi_entry_get_sdn(e);
  1830. rc = slapi_search_internal_get_entry( sdn, NULL, &entry, repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION));
  1831. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_entry_already_exists\n", 0, 0, 0 );
  1832. if (rc == LDAP_SUCCESS)
  1833. {
  1834. return entry;
  1835. }
  1836. else
  1837. {
  1838. return NULL;
  1839. }
  1840. }
  1841. #endif
  1842. static int
  1843. windows_delete_local_entry(Slapi_DN *sdn){
  1844. Slapi_PBlock *pb = NULL;
  1845. int return_value = 0;
  1846. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_delete_local_entry\n", 0, 0, 0 );
  1847. pb = slapi_pblock_new();
  1848. slapi_delete_internal_set_pb(pb, slapi_sdn_get_dn(sdn), NULL, NULL, repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), 0);
  1849. slapi_delete_internal_pb(pb);
  1850. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &return_value);
  1851. slapi_pblock_destroy(pb);
  1852. if (return_value) {
  1853. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  1854. "delete operation on local entry %s returned: %d\n", slapi_sdn_get_dn(sdn), return_value);
  1855. }
  1856. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_delete_local_entry: %d\n", return_value, 0, 0 );
  1857. return return_value;
  1858. }
  1859. /*
  1860. Before we send the modify to AD, we need to check to see if the mod still
  1861. applies - the entry in AD may have been modified, and those changes not sync'd
  1862. back to the DS, since the way winsync currently works is that it polls periodically
  1863. using DirSync for changes in AD - note that this does not guarantee that the mod
  1864. will apply cleanly, since there is still a small window of time between the time
  1865. we read the entry from AD and the time the mod op is sent, but doing this check
  1866. here should substantially reduce the chances of these types of out-of-sync problems
  1867. If we do find a mod that does not apply cleanly, we just discard it and log an
  1868. error message to that effect.
  1869. */
  1870. static int
  1871. mod_already_made(Private_Repl_Protocol *prp, Slapi_Mod *smod)
  1872. {
  1873. int retval = 0;
  1874. int op = 0;
  1875. const char *type = NULL;
  1876. const Slapi_Entry *ad_entry = windows_private_get_raw_entry(prp->agmt);
  1877. if (!slapi_mod_isvalid(smod)) { /* bogus */
  1878. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1879. "%s: mod_already_made: "
  1880. "modify operation is null - skipping.\n",
  1881. agmt_get_long_name(prp->agmt));
  1882. return 1;
  1883. }
  1884. if (!ad_entry) { /* mods cannot already have been made */
  1885. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1886. "%s: mod_already_made: "
  1887. "AD entry not found\n",
  1888. agmt_get_long_name(prp->agmt));
  1889. return retval; /* just allow - will probably fail later if entry really doesn't exist */
  1890. }
  1891. op = slapi_mod_get_operation(smod);
  1892. type = slapi_mod_get_type(smod);
  1893. if (SLAPI_IS_MOD_ADD(op)) { /* make sure value is not there */
  1894. struct berval *bv = NULL;
  1895. for (bv = slapi_mod_get_first_value(smod);
  1896. bv; bv = slapi_mod_get_next_value(smod)) {
  1897. Slapi_Value *sv = slapi_value_new();
  1898. slapi_value_init_berval(sv, bv); /* copies bv_val */
  1899. if (slapi_entry_attr_has_syntax_value(ad_entry, type, sv)) {
  1900. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1901. "%s: mod_already_made: "
  1902. "remote entry attr [%s] already has value [%s] - will not send.\n",
  1903. agmt_get_long_name(prp->agmt), type,
  1904. slapi_value_get_string(sv));
  1905. slapi_mod_remove_value(smod); /* removes the value at the current iterator pos */
  1906. }
  1907. slapi_value_free(&sv);
  1908. }
  1909. /* if all values were removed, no need to send the mod */
  1910. if (slapi_mod_get_num_values(smod) == 0) {
  1911. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1912. "%s: mod_already_made: "
  1913. "remote entry attr [%s] had all mod values removed - will not send.\n",
  1914. agmt_get_long_name(prp->agmt), type);
  1915. retval = 1;
  1916. }
  1917. } else if (SLAPI_IS_MOD_DELETE(op)) { /* make sure value or attr is there */
  1918. Slapi_Attr *attr = NULL;
  1919. /* if attribute does not exist, no need to send the delete */
  1920. if (slapi_entry_attr_find(ad_entry, type, &attr) || !attr) {
  1921. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1922. "%s: mod_already_made: "
  1923. "remote entry attr [%s] already deleted - will not send.\n",
  1924. agmt_get_long_name(prp->agmt), type);
  1925. retval = 1;
  1926. } else if (slapi_mod_get_num_values(smod) > 0) {
  1927. /* if attr exists, remove mods that have already been applied */
  1928. struct berval *bv = NULL;
  1929. for (bv = slapi_mod_get_first_value(smod);
  1930. bv; bv = slapi_mod_get_next_value(smod)) {
  1931. Slapi_Value *sv = slapi_value_new();
  1932. slapi_value_init_berval(sv, bv); /* copies bv_val */
  1933. if (!slapi_entry_attr_has_syntax_value(ad_entry, type, sv)) {
  1934. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1935. "%s: mod_already_made: "
  1936. "remote entry attr [%s] already deleted value [%s] - will not send.\n",
  1937. agmt_get_long_name(prp->agmt), type,
  1938. slapi_value_get_string(sv));
  1939. slapi_mod_remove_value(smod); /* removes the value at the current iterator pos */
  1940. }
  1941. slapi_value_free(&sv);
  1942. }
  1943. /* if all values were removed, no need to send the mod */
  1944. if (slapi_mod_get_num_values(smod) == 0) {
  1945. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1946. "%s: mod_already_made: "
  1947. "remote entry attr [%s] had all mod values removed - will not send.\n",
  1948. agmt_get_long_name(prp->agmt), type);
  1949. retval = 1;
  1950. }
  1951. } /* else if no values specified, this means delete the attribute */
  1952. } else { /* allow this mod */
  1953. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  1954. "%s: mod_already_made: "
  1955. "skipping mod op [%d]\n",
  1956. agmt_get_long_name(prp->agmt), op);
  1957. }
  1958. return retval;
  1959. }
  1960. /*
  1961. * Comparing the local_dn and the remote_dn, return the newsuperior.
  1962. * If to_windows is non-zero, the newsuperior is for the entry on AD.
  1963. * If to_windows is 0, the newsuperior is on DS.
  1964. *
  1965. * Caller must be responsible to free newsuperior.
  1966. *
  1967. * Return value: 0 if successful
  1968. * non zero (and *newsuperior is NULL) if failed
  1969. */
  1970. static int
  1971. windows_get_superior_change(Private_Repl_Protocol *prp,
  1972. Slapi_DN *local_dn, Slapi_DN *remote_dn,
  1973. char **newsuperior, int to_windows)
  1974. {
  1975. const Repl_Agmt *winrepl_agmt;
  1976. const char *remote_ndn = NULL; /* Normalized dn of the remote entry */
  1977. const char *local_ndn = NULL; /* Normalized dn of the local entry */
  1978. char *remote_pndn = NULL; /* Normalized parent dn of the remote entry */
  1979. char *local_pndn = NULL; /* Normalized parent dn of the local entry */
  1980. const char *remote_subtree = NULL; /* Normalized subtree of the remote entry */
  1981. const char *local_subtree = NULL; /* Normalized subtree of the local entry */
  1982. char *ptr = NULL;
  1983. int rc = -1;
  1984. if (NULL == newsuperior) {
  1985. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  1986. "windows_get_superior_change: newsuperior is NULL\n");
  1987. goto bail;
  1988. }
  1989. /* Check if modrdn with superior has happened on AD */
  1990. winrepl_agmt = prp->agmt;
  1991. remote_subtree =
  1992. slapi_sdn_get_ndn(windows_private_get_windows_subtree(winrepl_agmt));
  1993. local_subtree =
  1994. slapi_sdn_get_ndn(windows_private_get_directory_subtree(winrepl_agmt));
  1995. if (NULL == remote_subtree || NULL == local_subtree ||
  1996. '\0' == *remote_subtree || '\0' == *local_subtree) {
  1997. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  1998. "windows_get_superior_change: local subtree \"%s\" or "
  1999. "remote subtree \"%s\" is empty\n",
  2000. local_subtree?local_subtree:"empty",
  2001. remote_subtree?remote_subtree:"empty");
  2002. goto bail;
  2003. }
  2004. remote_ndn = slapi_sdn_get_ndn(remote_dn);
  2005. local_ndn = slapi_sdn_get_ndn(local_dn);
  2006. if (NULL == remote_ndn || NULL == local_ndn ||
  2007. '\0' == *remote_ndn || '\0' == *local_ndn) {
  2008. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  2009. "windows_get_superior_change: local dn \"%s\" or "
  2010. "remote dn \"%s\" is empty\n",
  2011. local_ndn?local_ndn:"empty", remote_ndn?remote_ndn:"empty");
  2012. goto bail;
  2013. }
  2014. remote_pndn = slapi_dn_parent((const char *)remote_ndn); /* strdup'ed */
  2015. local_pndn = slapi_dn_parent((const char *)local_ndn); /* strdup'ed */
  2016. if (NULL == remote_pndn || NULL == local_pndn ||
  2017. '\0' == *remote_pndn || '\0' == *local_pndn) {
  2018. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  2019. "windows_get_superior_change: local parent dn \"%s\" or "
  2020. "remote parent dn \"%s\" is empty\n",
  2021. local_pndn?local_pndn:"empty",
  2022. remote_pndn?remote_pndn:"empty");
  2023. goto bail;
  2024. }
  2025. ptr = strstr(remote_pndn, remote_subtree);
  2026. if (ptr) {
  2027. *ptr = '\0'; /* if ptr != remote_pndn, remote_pndn ends with ',' */
  2028. ptr = strstr(local_pndn, local_subtree);
  2029. if (ptr) {
  2030. *ptr = '\0'; /* if ptr != local_pndn, local_pndn ends with ',' */
  2031. if (0 != strcmp(remote_pndn, local_pndn)) {
  2032. /* the remote parent is different from the local parent */
  2033. if (to_windows) {
  2034. *newsuperior =
  2035. PR_smprintf("%s%s", local_pndn, remote_subtree);
  2036. } else {
  2037. *newsuperior =
  2038. PR_smprintf("%s%s", remote_pndn, local_subtree);
  2039. }
  2040. rc = 0;
  2041. }
  2042. } else {
  2043. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  2044. "windows_get_superior_change: local parent \"%s\" is not in "
  2045. "DirectoryReplicaSubtree \"%s\"\n", local_pndn, local_subtree);
  2046. }
  2047. } else {
  2048. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  2049. "windows_get_superior_change: remote parent \"%s\" is not in "
  2050. "WindowsReplicaSubtree \"%s\"\n", remote_pndn, remote_subtree);
  2051. }
  2052. bail:
  2053. slapi_ch_free_string(&remote_pndn);
  2054. slapi_ch_free_string(&local_pndn);
  2055. return rc;
  2056. }
  2057. static int
  2058. windows_check_mods_for_rdn_change(Private_Repl_Protocol *prp, LDAPMod **original_mods,
  2059. Slapi_Entry *local_entry, Slapi_DN *remote_dn, char **newrdn)
  2060. {
  2061. int ret = 0;
  2062. int need_rename = 0;
  2063. int got_entry = 0;
  2064. Slapi_Entry *remote_entry = NULL;
  2065. Slapi_Attr *remote_rdn_attr = NULL;
  2066. Slapi_Value *remote_rdn_val = NULL;
  2067. Slapi_Mods smods = {0};
  2068. Slapi_Mod *smod = slapi_mod_new();
  2069. Slapi_Mod *last_smod = smod;
  2070. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_check_mods_for_rdn_change\n", 0, 0, 0 );
  2071. /* Iterate through the original mods, looking for a modification to the RDN attribute */
  2072. slapi_mods_init_byref(&smods, original_mods);
  2073. smod = slapi_mods_get_first_smod(&smods, last_smod);
  2074. while(smod) {
  2075. /* Check if this is modifying the naming attribute (cn) */
  2076. if (slapi_attr_types_equivalent(slapi_mod_get_type(smod), "cn")) {
  2077. /* Fetch the remote entry so we can compare the new values
  2078. * against the existing remote value. We only need to do
  2079. * this once for all mods. */
  2080. if (!got_entry) {
  2081. int free_entry = 0;
  2082. /* See if we have already fetched the remote entry.
  2083. * If not, we just fetch it ourselves. */
  2084. if ((remote_entry = windows_private_get_raw_entry(prp->agmt)) == NULL) {
  2085. windows_get_remote_entry(prp, remote_dn, &remote_entry);
  2086. free_entry = 1;
  2087. }
  2088. if (remote_entry) {
  2089. /* Fetch and duplicate the cn attribute so we can perform comparisions */
  2090. slapi_entry_attr_find(remote_entry, "cn", &remote_rdn_attr);
  2091. if (remote_rdn_attr) {
  2092. remote_rdn_attr = slapi_attr_dup(remote_rdn_attr);
  2093. slapi_attr_first_value(remote_rdn_attr, &remote_rdn_val);
  2094. }
  2095. /* We only want to free the entry if we fetched it ourselves
  2096. * by calling windows_get_remote_entry(). */
  2097. if (free_entry) {
  2098. slapi_entry_free(remote_entry);
  2099. }
  2100. }
  2101. got_entry = 1;
  2102. /* If we didn't get the remote value for some odd reason, just bail out. */
  2103. if (!remote_rdn_val) {
  2104. slapi_mod_done(smod);
  2105. goto done;
  2106. }
  2107. }
  2108. if (SLAPI_IS_MOD_REPLACE(slapi_mod_get_operation(smod))) {
  2109. /* For a replace, we just need to check if the old value that AD
  2110. * has is still present after the operation. If not, we rename
  2111. * the entry in AD using the first new value as the RDN. */
  2112. Slapi_Value *new_val = NULL;
  2113. struct berval *bval = NULL;
  2114. /* Assume that we're going to need to do a rename. */
  2115. ret = 1;
  2116. /* Get the first new value, which is to be used as the RDN if we decide
  2117. * that a rename is necessary. */
  2118. bval = slapi_mod_get_first_value(smod);
  2119. if (bval && bval->bv_val) {
  2120. /* Fill in new RDN to return to caller. */
  2121. slapi_ch_free_string(newrdn);
  2122. *newrdn = slapi_ch_smprintf("cn=%s", bval->bv_val);
  2123. /* Loop through all new values to check if they match
  2124. * the value present in AD. */
  2125. do {
  2126. new_val = slapi_value_new_berval(bval);
  2127. if (slapi_value_compare(remote_rdn_attr, remote_rdn_val, new_val) == 0) {
  2128. /* We have a match. This means we don't want to rename the entry in AD. */
  2129. slapi_ch_free_string(newrdn);
  2130. slapi_value_free(&new_val);
  2131. ret = 0;
  2132. break;
  2133. }
  2134. slapi_value_free(&new_val);
  2135. bval = slapi_mod_get_next_value(smod);
  2136. } while (bval && bval->bv_val);
  2137. }
  2138. } else if (SLAPI_IS_MOD_DELETE(slapi_mod_get_operation(smod))) {
  2139. /* We need to check if the cn in AD is the value being deleted. If
  2140. * so, set a flag saying that we will need to do a rename. We will either
  2141. * get a new value added from another mod in this op, or we will need to
  2142. * use an old value that is left over after the delete operation. */
  2143. if (slapi_mod_get_num_values(smod) == 0) {
  2144. /* All values are being deleted, so a rename will be needed. One
  2145. * of the other mods will be adding the new values(s). */
  2146. need_rename = 1;
  2147. } else {
  2148. Slapi_Value *del_val = NULL;
  2149. struct berval *bval = NULL;
  2150. bval = slapi_mod_get_first_value(smod);
  2151. while (bval && bval->bv_val) {
  2152. /* Is this value the same one that is used as the RDN in AD? */
  2153. del_val = slapi_value_new_berval(bval);
  2154. if (slapi_value_compare(remote_rdn_attr, remote_rdn_val, del_val) == 0) {
  2155. /* We have a match. This means we need to rename the entry in AD. */
  2156. need_rename = 1;
  2157. slapi_value_free(&del_val);
  2158. break;
  2159. }
  2160. slapi_value_free(&del_val);
  2161. bval = slapi_mod_get_next_value(smod);
  2162. }
  2163. }
  2164. } else if (SLAPI_IS_MOD_ADD(slapi_mod_get_operation(smod))) {
  2165. /* We only need to care about an add if the old value was deleted. */
  2166. if (need_rename) {
  2167. /* Just grab the first new value and use it to create the new RDN. */
  2168. struct berval *bval = slapi_mod_get_first_value(smod);
  2169. if (bval && bval->bv_val) {
  2170. /* Fill in new RDN to return to caller. */
  2171. slapi_ch_free_string(newrdn);
  2172. *newrdn = slapi_ch_smprintf("cn=%s", bval->bv_val);
  2173. need_rename = 0;
  2174. ret = 1;
  2175. }
  2176. }
  2177. }
  2178. }
  2179. /* Get the next mod from this op. */
  2180. slapi_mod_done(smod);
  2181. /* Need to prevent overwriting old smod with NULL return value and causing a leak. */
  2182. smod = slapi_mods_get_next_smod(&smods, last_smod);
  2183. }
  2184. done:
  2185. /* We're done with the mods and the copied cn attr from the remote entry. */
  2186. slapi_attr_free(&remote_rdn_attr);
  2187. if (last_smod) {
  2188. slapi_mod_free(&last_smod);
  2189. }
  2190. slapi_mods_done (&smods);
  2191. if (need_rename) {
  2192. /* We need to perform a rename, but we didn't get the value for the
  2193. * new RDN from this operation. We fetch the first value from the local
  2194. * entry to create the new RDN. */
  2195. if (local_entry) {
  2196. char *newval = slapi_entry_attr_get_charptr(local_entry, "cn");
  2197. if (newval) {
  2198. /* Fill in new RDN to return to caller. */
  2199. slapi_ch_free_string(newrdn);
  2200. *newrdn = slapi_ch_smprintf("cn=%s", newval);
  2201. slapi_ch_free_string(&newval);
  2202. ret = 1;
  2203. }
  2204. }
  2205. }
  2206. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_check_mods_for_rdn_change: %d\n", ret, 0, 0 );
  2207. return ret;
  2208. }
  2209. static void
  2210. windows_map_mods_for_replay(Private_Repl_Protocol *prp,LDAPMod **original_mods, LDAPMod ***returned_mods, int is_user, char** password)
  2211. {
  2212. Slapi_Mods smods = {0};
  2213. Slapi_Mods mapped_smods = {0};
  2214. LDAPMod *mod = NULL;
  2215. int is_nt4 = windows_private_get_isnt4(prp->agmt);
  2216. Slapi_Mod *mysmod = NULL;
  2217. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_map_mods_for_replay\n", 0, 0, 0 );
  2218. /* Iterate through the original mods, looking each attribute type up in the maps for either user or group */
  2219. slapi_mods_init_byref(&smods, original_mods);
  2220. slapi_mods_init(&mapped_smods,10);
  2221. mod = slapi_mods_get_first_mod(&smods);
  2222. while(mod)
  2223. {
  2224. char *attr_type = mod->mod_type;
  2225. int mapdn = 0;
  2226. /* Check to see if this attribute is passed through */
  2227. if (is_straight_mapped_attr(attr_type,is_user,is_nt4)) {
  2228. /* If this attribute is single-valued in AD,
  2229. * we only want to send the first value. */
  2230. if (is_single_valued_attr(attr_type)) {
  2231. Slapi_Mod smod;
  2232. slapi_mod_init_byref(&smod,mod);
  2233. /* Check if there is more than one value */
  2234. if (slapi_mod_get_num_values(&smod) > 1) {
  2235. slapi_mod_get_first_value(&smod);
  2236. /* Remove all values except for the first */
  2237. while (slapi_mod_get_next_value(&smod)) {
  2238. /* This modifies the bvalues in the mod itself */
  2239. slapi_mod_remove_value(&smod);
  2240. }
  2241. }
  2242. slapi_mod_done(&smod);
  2243. }
  2244. /* The initials attribute is a special case. AD has a constraint
  2245. * that limits the value length. If we're sending a change to
  2246. * the initials attribute to AD, we trim if neccessary.
  2247. */
  2248. if (0 == slapi_attr_type_cmp(attr_type, "initials", SLAPI_TYPE_CMP_SUBTYPE)) {
  2249. int i;
  2250. for (i = 0; mod->mod_bvalues[i] != NULL; i++) {
  2251. /* If > AD_INITIALS_LENGTH, trim the value */
  2252. if (mod->mod_bvalues[i]->bv_len > AD_INITIALS_LENGTH) {
  2253. mod->mod_bvalues[i]->bv_val[AD_INITIALS_LENGTH] = '\0';
  2254. mod->mod_bvalues[i]->bv_len = AD_INITIALS_LENGTH;
  2255. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  2256. "%s: windows_map_mods_for_replay: "
  2257. "Trimming initials attribute to %d characters.\n",
  2258. agmt_get_long_name(prp->agmt), AD_INITIALS_LENGTH);
  2259. }
  2260. }
  2261. }
  2262. /* create the new smod to add to the mapped_smods */
  2263. mysmod = slapi_mod_new();
  2264. slapi_mod_init_byval(mysmod, mod); /* copy contents */
  2265. } else
  2266. {
  2267. char *mapped_type = NULL;
  2268. /* Check if this mod has its attribute type mapped */
  2269. windows_map_attr_name(attr_type,1,is_user,0,&mapped_type, &mapdn);
  2270. if (mapped_type)
  2271. {
  2272. /* If so copy over the mod with new type name */
  2273. if (mapdn)
  2274. {
  2275. Slapi_ValueSet *mapped_values = NULL;
  2276. Slapi_ValueSet *vs = NULL;
  2277. Slapi_Mod smod;
  2278. vs = slapi_valueset_new();
  2279. slapi_mod_init_byref(&smod,mod);
  2280. slapi_valueset_set_from_smod(vs, &smod);
  2281. map_dn_values(prp,vs,&mapped_values, 1 /* to windows */,0);
  2282. if (mapped_values)
  2283. {
  2284. mysmod = slapi_mod_new();
  2285. slapi_mod_init_valueset_byval(mysmod, mod->mod_op, mapped_type, mapped_values);
  2286. slapi_valueset_free(mapped_values);
  2287. mapped_values = NULL;
  2288. } else
  2289. {
  2290. /* this might be a del: mod, in which case there are no values */
  2291. if (mod->mod_op & LDAP_MOD_DELETE)
  2292. {
  2293. mysmod = slapi_mod_new();
  2294. slapi_mod_init(mysmod, 0);
  2295. slapi_mod_set_operation(mysmod, LDAP_MOD_DELETE|LDAP_MOD_BVALUES);
  2296. slapi_mod_set_type(mysmod, mapped_type);
  2297. }
  2298. }
  2299. slapi_mod_done(&smod);
  2300. slapi_valueset_free(vs);
  2301. } else
  2302. {
  2303. /* If this attribute is single-valued in AD,
  2304. * we only want to send the first value. */
  2305. if (is_single_valued_attr(mapped_type)) {
  2306. Slapi_Mod smod;
  2307. slapi_mod_init_byref(&smod,mod);
  2308. /* Check if there is more than one value */
  2309. if (slapi_mod_get_num_values(&smod) > 1) {
  2310. slapi_mod_get_first_value(&smod);
  2311. /* Remove all values except for the first */
  2312. while (slapi_mod_get_next_value(&smod)) {
  2313. /* This modifies the bvalues in the mod itself */
  2314. slapi_mod_remove_value(&smod);
  2315. }
  2316. }
  2317. slapi_mod_done(&smod);
  2318. }
  2319. /* create the new smod to add to the mapped_smods */
  2320. mysmod = slapi_mod_new();
  2321. slapi_mod_init_byval(mysmod, mod); /* copy contents */
  2322. slapi_mod_set_type(mysmod, mapped_type);
  2323. }
  2324. slapi_ch_free_string(&mapped_type);
  2325. } else
  2326. {
  2327. /* password mods are treated specially */
  2328. if ((0 == slapi_attr_type_cmp(attr_type, PSEUDO_ATTR_UNHASHEDUSERPASSWORD, SLAPI_TYPE_CMP_SUBTYPE)) &&
  2329. mod && mod->mod_bvalues && mod->mod_bvalues[0] && mod->mod_bvalues[0]->bv_val)
  2330. {
  2331. char *password_value = NULL;
  2332. password_value = mod->mod_bvalues[0]->bv_val;
  2333. /* We need to check if the first character of password_value is an
  2334. * opening brace since strstr will simply return it's first argument
  2335. * if it is an empty string. */
  2336. if (password_value && (*password_value == '{')) {
  2337. if (strchr( password_value, '}' )) {
  2338. /* A storage scheme is present. Check if it's the
  2339. * clear storage scheme. */
  2340. if ((strlen(password_value) >= PASSWD_CLEAR_PREFIX_LEN + 1) &&
  2341. (strncasecmp(password_value, PASSWD_CLEAR_PREFIX, PASSWD_CLEAR_PREFIX_LEN) == 0)) {
  2342. /* This password is in clear text. Strip off the clear prefix
  2343. * and sync it. */
  2344. *password = slapi_ch_strdup(password_value + PASSWD_CLEAR_PREFIX_LEN);
  2345. } else {
  2346. /* This password is stored in a non-cleartext format.
  2347. * We can only sync cleartext passwords. */
  2348. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  2349. "%s: windows_create_remote_entry: "
  2350. "Password is already hashed. Not syncing.\n",
  2351. agmt_get_long_name(prp->agmt));
  2352. }
  2353. } else {
  2354. /* This password doesn't have a storage prefix but
  2355. * just happens to start with the '{' character. We'll
  2356. * assume that it's just a cleartext password without
  2357. * the proper storage prefix. */
  2358. *password = slapi_ch_strdup(password_value);
  2359. }
  2360. } else {
  2361. /* This password has no storage prefix, or the password is empty */
  2362. *password = slapi_ch_strdup(password_value);
  2363. }
  2364. }
  2365. }
  2366. }
  2367. /* Otherwise we do not copy this mod at all */
  2368. if (mysmod && !mod_already_made(prp, mysmod)) { /* make sure this mod is still valid to send */
  2369. slapi_mods_add_ldapmod(&mapped_smods, slapi_mod_get_ldapmod_passout(mysmod));
  2370. }
  2371. if (mysmod) {
  2372. slapi_mod_free(&mysmod);
  2373. }
  2374. mod = slapi_mods_get_next_mod(&smods);
  2375. }
  2376. slapi_mods_done (&smods);
  2377. /* Extract the mods for the caller */
  2378. *returned_mods = slapi_mods_get_ldapmods_passout(&mapped_smods);
  2379. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_map_mods_for_replay\n", 0, 0, 0 );
  2380. }
  2381. /* Returns non-zero if the attribute value sets are identical. If you want to
  2382. * compare the entire attribute value, set n to 0. You can compare only the
  2383. * first n characters of the values by passing in the legth as n. */
  2384. static int
  2385. attr_compare_equal(Slapi_Attr *a, Slapi_Attr *b, int n)
  2386. {
  2387. /* For now only handle single values */
  2388. Slapi_Value *va = NULL;
  2389. Slapi_Value *vb = NULL;
  2390. int num_a = 0;
  2391. int num_b = 0;
  2392. int match = 1;
  2393. slapi_attr_get_numvalues(a,&num_a);
  2394. slapi_attr_get_numvalues(b,&num_b);
  2395. if (num_a == num_b) {
  2396. slapi_attr_first_value(a, &va);
  2397. slapi_attr_first_value(b, &vb);
  2398. /* If either val is less than n, then check if the length, then values are
  2399. * equal. If both are n or greater, then only compare the first n chars.
  2400. * If n is 0, then just compare the entire attribute. */
  2401. if ((va->bv.bv_len < n) || (vb->bv.bv_len < n) || (n == 0)) {
  2402. if (va->bv.bv_len == vb->bv.bv_len) {
  2403. if (slapi_attr_value_find(b, slapi_value_get_berval(va)) != 0) {
  2404. match = 0;
  2405. }
  2406. } else {
  2407. match = 0;
  2408. }
  2409. } else if (0 != memcmp(va->bv.bv_val, vb->bv.bv_val, n)) {
  2410. match = 0;
  2411. }
  2412. } else {
  2413. match = 0;
  2414. }
  2415. return match;
  2416. }
  2417. /* Returns non-zero if all of the values of attribute a are contained in attribute b.
  2418. * You can compare only the first n characters of the values by passing in the length
  2419. * as n. If you want to compare the entire attribute value, set n to 0. */
  2420. static int
  2421. attr_compare_present(Slapi_Attr *a, Slapi_Attr *b, int n)
  2422. {
  2423. int match = 1;
  2424. int i = 0;
  2425. int j = 0;
  2426. Slapi_Value *va = NULL;
  2427. Slapi_Value *vb = NULL;
  2428. /* Iterate through values in attr a and search for each in attr b */
  2429. for (i = slapi_attr_first_value(a, &va); va && (i != -1);
  2430. i = slapi_attr_next_value(a, i, &va)) {
  2431. if (n == 0) {
  2432. /* Compare the entire attribute value */
  2433. if (slapi_attr_value_find(b, slapi_value_get_berval(va)) != 0) {
  2434. match = 0;
  2435. goto bail;
  2436. }
  2437. } else {
  2438. /* Only compare up the values up to the specified length. */
  2439. int found = 0;
  2440. for (j = slapi_attr_first_value(b, &vb); vb && (j != -1);
  2441. j = slapi_attr_next_value(b, j, &vb)) {
  2442. /* If either val is less than n, then check if the length, then values are
  2443. * equal. If both are n or greater, then only compare the first n chars. */
  2444. if ((va->bv.bv_len < n) || (vb->bv.bv_len < n)) {
  2445. if (va->bv.bv_len == vb->bv.bv_len) {
  2446. if (0 == memcmp(va->bv.bv_val, vb->bv.bv_val, va->bv.bv_len)) {
  2447. found = 1;
  2448. }
  2449. }
  2450. } else if (0 == memcmp(va->bv.bv_val, vb->bv.bv_val, n)) {
  2451. found = 1;
  2452. }
  2453. }
  2454. /* If we didn't find this value from attr a in attr b, we're done. */
  2455. if (!found) {
  2456. match = 0;
  2457. goto bail;
  2458. }
  2459. }
  2460. }
  2461. bail:
  2462. return match;
  2463. }
  2464. /* Helper functions for dirsync result processing */
  2465. /* Is this entry a tombstone ? */
  2466. static int
  2467. is_tombstone(Private_Repl_Protocol *prp, Slapi_Entry *e)
  2468. {
  2469. int retval = 0;
  2470. if ( (slapi_filter_test_simple( e, (Slapi_Filter*)windows_private_get_deleted_filter(prp->agmt) ) == 0) )
  2471. {
  2472. retval = 1;
  2473. }
  2474. return retval;
  2475. }
  2476. #define ENTRY_NOTFOUND -1
  2477. #define ENTRY_NOT_UNIQUE -2
  2478. /* Search for an entry in AD */
  2479. static int
  2480. find_entry_by_attr_value_remote(const char *attribute, const char *value, Slapi_Entry **e, Private_Repl_Protocol *prp)
  2481. {
  2482. int retval = 0;
  2483. ConnResult cres = 0;
  2484. char *filter = NULL;
  2485. const char *searchbase = NULL;
  2486. Slapi_Entry *found_entry = NULL;
  2487. filter = PR_smprintf("(%s=%s)",attribute,value);
  2488. searchbase = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
  2489. cres = windows_search_entry(prp->conn, (char*)searchbase, filter, &found_entry);
  2490. if (cres)
  2491. {
  2492. retval = -1;
  2493. } else
  2494. {
  2495. if (found_entry)
  2496. {
  2497. *e = found_entry;
  2498. }
  2499. }
  2500. if (filter)
  2501. {
  2502. PR_smprintf_free(filter);
  2503. filter = NULL;
  2504. }
  2505. return retval;
  2506. }
  2507. /* Search for an entry in AD by DN */
  2508. static int
  2509. windows_get_remote_entry (Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,Slapi_Entry **remote_entry)
  2510. {
  2511. int retval = 0;
  2512. ConnResult cres = 0;
  2513. char *filter = "(objectclass=*)";
  2514. const char *searchbase = NULL;
  2515. Slapi_Entry *found_entry = NULL;
  2516. searchbase = slapi_sdn_get_dn(remote_dn);
  2517. cres = windows_search_entry(prp->conn, (char*)searchbase, filter, &found_entry);
  2518. if (cres)
  2519. {
  2520. retval = -1;
  2521. } else
  2522. {
  2523. if (found_entry)
  2524. {
  2525. *remote_entry = found_entry;
  2526. }
  2527. }
  2528. return retval;
  2529. }
  2530. /* Search for a tombstone entry in AD by DN */
  2531. static int
  2532. windows_get_remote_tombstone (Private_Repl_Protocol *prp, const Slapi_DN* remote_dn,Slapi_Entry **remote_entry)
  2533. {
  2534. int retval = 0;
  2535. ConnResult cres = 0;
  2536. char *filter = "(objectclass=*)";
  2537. const char *searchbase = NULL;
  2538. Slapi_Entry *found_entry = NULL;
  2539. LDAPControl *server_controls[2];
  2540. /* We need to send the "Return Deleted Objects" control to search
  2541. * for tombstones. */
  2542. slapi_build_control(REPL_RETURN_DELETED_OBJS_CONTROL_OID, NULL, PR_TRUE,
  2543. &server_controls[0]);
  2544. server_controls[1] = NULL;
  2545. searchbase = slapi_sdn_get_dn(remote_dn);
  2546. cres = windows_search_entry_ext(prp->conn, (char*)searchbase, filter,
  2547. &found_entry, server_controls);
  2548. if (cres) {
  2549. retval = -1;
  2550. } else {
  2551. if (found_entry) {
  2552. *remote_entry = found_entry;
  2553. }
  2554. }
  2555. ldap_control_free(server_controls[0]);
  2556. return retval;
  2557. }
  2558. /* Reanimate a tombstone in AD. Returns 0 on success, otherwise you get the
  2559. * LDAP return code from the modify operation. */
  2560. static int
  2561. windows_reanimate_tombstone(Private_Repl_Protocol *prp, const Slapi_DN* tombstone_dn, const char* new_dn)
  2562. {
  2563. int retval = 0;
  2564. LDAPControl *server_controls[2];
  2565. Slapi_Mods smods = {0};
  2566. /* We need to send the "Return Deleted Objects" control to modify
  2567. * tombstone entries. */
  2568. slapi_build_control(REPL_RETURN_DELETED_OBJS_CONTROL_OID, NULL, PR_TRUE,
  2569. &server_controls[0]);
  2570. server_controls[1] = NULL;
  2571. /* To reanimate a tombstone in AD, you need to send a modify
  2572. * operation that does two things. It must remove the isDeleted
  2573. * attribute from the entry and it must modify the DN. This DN
  2574. * does not have to be the same place in the tree that the entry
  2575. * previously existed. */
  2576. slapi_mods_init (&smods, 0);
  2577. slapi_mods_add_mod_values(&smods, LDAP_MOD_DELETE, "isDeleted", NULL);
  2578. slapi_mods_add_string(&smods, LDAP_MOD_REPLACE, "distinguishedName", new_dn);
  2579. retval = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(tombstone_dn),
  2580. slapi_mods_get_ldapmods_byref(&smods), server_controls, NULL );
  2581. slapi_mods_done(&smods);
  2582. ldap_control_free(server_controls[0]);
  2583. return retval;
  2584. }
  2585. static int
  2586. find_entry_by_attr_value(const char *attribute, const char *value, Slapi_Entry **e, const Repl_Agmt *ra)
  2587. {
  2588. Slapi_PBlock *pb = slapi_pblock_new();
  2589. Slapi_Entry **entries = NULL, **ep = NULL;
  2590. Slapi_Entry *entry_found = NULL;
  2591. char *query = NULL;
  2592. int found_or_not = ENTRY_NOTFOUND;
  2593. int rval = 0;
  2594. const char *subtree_dn = NULL;
  2595. int not_unique = 0;
  2596. char *subtree_dn_copy = NULL;
  2597. int scope = LDAP_SCOPE_SUBTREE;
  2598. char **attrs = NULL;
  2599. LDAPControl **server_controls = NULL;
  2600. if (pb == NULL)
  2601. goto done;
  2602. query = slapi_ch_smprintf("(%s=%s)", attribute, value);
  2603. if (query == NULL)
  2604. goto done;
  2605. subtree_dn = slapi_sdn_get_dn(windows_private_get_directory_subtree(ra));
  2606. subtree_dn_copy = slapi_ch_strdup(subtree_dn);
  2607. winsync_plugin_call_pre_ds_search_entry_cb(ra, NULL, &subtree_dn_copy, &scope, &query,
  2608. &attrs, &server_controls);
  2609. slapi_search_internal_set_pb(pb, subtree_dn_copy,
  2610. scope, query, attrs, 0, server_controls, NULL,
  2611. (void *)plugin_get_default_component_id(), 0);
  2612. slapi_search_internal_pb(pb);
  2613. slapi_ch_free_string(&subtree_dn_copy);
  2614. slapi_ch_free_string(&query);
  2615. slapi_ch_array_free(attrs);
  2616. attrs = NULL;
  2617. ldap_controls_free(server_controls);
  2618. server_controls = NULL;
  2619. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &rval);
  2620. if (rval != LDAP_SUCCESS)
  2621. {
  2622. goto done;
  2623. }
  2624. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries);
  2625. if ((entries == NULL) || (entries[0] == NULL))
  2626. {
  2627. goto done;
  2628. }
  2629. entry_found = entries[0];
  2630. for (ep = entries; *ep; ep++) {
  2631. if (not_unique)
  2632. {
  2633. found_or_not = ENTRY_NOT_UNIQUE;
  2634. }
  2635. not_unique = 1;
  2636. }
  2637. done:
  2638. if (entry_found && (found_or_not != ENTRY_NOT_UNIQUE))
  2639. {
  2640. found_or_not = 0;
  2641. *e = slapi_entry_dup(entry_found);
  2642. }
  2643. if (pb)
  2644. {
  2645. slapi_free_search_results_internal(pb);
  2646. slapi_pblock_destroy(pb);
  2647. }
  2648. return found_or_not;
  2649. }
  2650. static int
  2651. find_entry_by_username(const char *username, Slapi_Entry **e, const Repl_Agmt *ra)
  2652. {
  2653. return find_entry_by_attr_value("ntUserDomainId",username,e,ra);
  2654. }
  2655. /* Find an entry in the local server given its GUID, or return ENTRY_NOTFOUND */
  2656. static int
  2657. find_entry_by_guid(const char *guid, Slapi_Entry **e, const Repl_Agmt *ra)
  2658. {
  2659. return find_entry_by_attr_value("ntUniqueId",guid,e,ra);
  2660. }
  2661. /* Remove dashes from a GUID string. */
  2662. static void
  2663. dedash_guid(char *str)
  2664. {
  2665. char *p = str;
  2666. char c = '\0';
  2667. while ((c = *p))
  2668. {
  2669. if ('-' == c)
  2670. {
  2671. /* Move on down please */
  2672. char *q = p;
  2673. char *r = q + 1;
  2674. while (*r)
  2675. {
  2676. *q++ = *r++;
  2677. }
  2678. *q = '\0';
  2679. }
  2680. p++;
  2681. }
  2682. }
  2683. /* Add dashes into a GUID string. If the guid is not formatted properly,
  2684. * we will free it and set the pointer to NULL. */
  2685. static void
  2686. dash_guid(char **str)
  2687. {
  2688. if (strlen(*str) == NTUNIQUEID_LENGTH) {
  2689. char *p = NULL;
  2690. /* Add extra room for the dashes */
  2691. *str = slapi_ch_realloc(*str, AD_GUID_LENGTH + 1);
  2692. /* GUID needs to be in 8-4-4-4-12 format */
  2693. p = *str + 23;
  2694. memmove(p + 1, *str + 20, 12);
  2695. *p = '-';
  2696. p = *str + 18;
  2697. memmove(p + 1, *str + 16, 4);
  2698. *p = '-';
  2699. p = *str + 13;
  2700. memmove(p + 1, *str + 12, 4);
  2701. *p = '-';
  2702. p = *str + 8;
  2703. memmove(p + 1, *str + 8, 4);
  2704. *p = '-';
  2705. p = *str + 36;
  2706. *p = '\0';
  2707. } else {
  2708. /* This GUID does not appear to be valid */
  2709. slapi_ch_free_string(str);
  2710. }
  2711. }
  2712. /* For reasons not clear, the GUID returned in the tombstone DN is all
  2713. * messed up, like the guy in the movie 'the fly' after he want in the tranporter device */
  2714. static void
  2715. decrypt_guid(char *guid)
  2716. {
  2717. static int decrypt_offsets[] = {6,7,4,5,2,3,0,1,10,11,8,9,14,15,12,13,16,17,18,19,
  2718. 20,21,22,23,24,25,26,27,28,29,30,31};
  2719. char *p = guid;
  2720. int i = 0;
  2721. char *cpy = slapi_ch_strdup(guid);
  2722. while (*p && i < (sizeof(decrypt_offsets)/sizeof(int)))
  2723. {
  2724. *p = cpy[decrypt_offsets[i]];
  2725. p++;
  2726. i++;
  2727. }
  2728. slapi_ch_free_string(&cpy);
  2729. }
  2730. static char*
  2731. extract_guid_from_tombstone_dn(const char *dn)
  2732. {
  2733. char *guid = NULL;
  2734. char *colon_offset = NULL;
  2735. char *comma_offset = NULL;
  2736. /* example DN of tombstone:
  2737. "CN=WDel Userdb1\\\nDEL:551706bc-ecf2-4b38-9284-9a8554171d69,CN=Deleted Objects,DC=magpie,DC=com" */
  2738. /* First find the 'DEL:' */
  2739. if ((colon_offset = strchr(dn,':'))) {
  2740. /* Then scan forward to the next ',' */
  2741. comma_offset = strchr(colon_offset,',');
  2742. }
  2743. /* The characters inbetween are the GUID, copy them
  2744. * to a new string and return to the caller */
  2745. if (comma_offset && colon_offset && comma_offset > colon_offset) {
  2746. guid = slapi_ch_malloc(comma_offset - colon_offset);
  2747. strncpy(guid,colon_offset+1,(comma_offset-colon_offset)-1);
  2748. guid[comma_offset-colon_offset-1] = '\0';
  2749. /* Finally remove the dashes since we don't store them on our side */
  2750. dedash_guid(guid);
  2751. decrypt_guid(guid);
  2752. }
  2753. return guid;
  2754. }
  2755. static char *
  2756. convert_to_hex(Slapi_Value *val)
  2757. {
  2758. int offset = 0;
  2759. const struct berval *bvp = NULL;
  2760. int length = 0;
  2761. char *result = NULL;
  2762. bvp = slapi_value_get_berval(val);
  2763. if (bvp)
  2764. {
  2765. char *new_buffer = NULL;
  2766. length = bvp->bv_len;
  2767. for (offset = 0; offset < length; offset++)
  2768. {
  2769. unsigned char byte = ((unsigned char*)(bvp->bv_val))[offset];
  2770. new_buffer = PR_sprintf_append(new_buffer, "%02x", byte );
  2771. }
  2772. if (new_buffer)
  2773. {
  2774. result = new_buffer;
  2775. }
  2776. }
  2777. return result;
  2778. }
  2779. /* Given a local entry, map it to it's AD tombstone DN. An AD
  2780. * tombstone DN is formatted like:
  2781. *
  2782. * cn=<cn>\0ADEL:<guid>,cn=Deleted Objects,<suffix>
  2783. *
  2784. * This function will allocate a new Slapi_DN. It is up to the
  2785. * caller to free it when they are finished with it. */
  2786. static int
  2787. map_windows_tombstone_dn(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *exists)
  2788. {
  2789. int rc = 0;
  2790. char *cn = NULL;
  2791. char *guid = NULL;
  2792. const char *suffix = NULL;
  2793. char *tombstone_dn = NULL;
  2794. Slapi_Entry *tombstone = NULL;
  2795. /* Initialize the output values */
  2796. *dn = NULL;
  2797. *exists = 0;
  2798. cn = slapi_entry_attr_get_charptr(e,"cn");
  2799. if (!cn) {
  2800. cn = slapi_entry_attr_get_charptr(e,"ntuserdomainid");
  2801. }
  2802. guid = slapi_entry_attr_get_charptr(e,"ntUniqueId");
  2803. if (guid) {
  2804. /* the GUID is in a different form in the tombstone DN, so
  2805. * we need to transform it from the way we store it. */
  2806. decrypt_guid(guid);
  2807. dash_guid(&guid);
  2808. }
  2809. /* The tombstone suffix discards any containers, so we need
  2810. * to trim the DN to only dc components. */
  2811. if ((suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt)))) {
  2812. /* If this isn't found, it is treated as an error below. */
  2813. suffix = (const char *) PL_strcasestr(suffix,"dc=");
  2814. }
  2815. if (cn && guid && suffix) {
  2816. tombstone_dn = PR_smprintf("cn=%s\\0ADEL:%s,cn=Deleted Objects,%s",
  2817. cn, guid, suffix);
  2818. /* Hand off the memory to the Slapi_DN */
  2819. *dn = slapi_sdn_new_dn_passin(tombstone_dn);
  2820. windows_get_remote_tombstone(prp, *dn, &tombstone);
  2821. if (tombstone) {
  2822. *exists = 1;
  2823. slapi_entry_free(tombstone);
  2824. }
  2825. } else {
  2826. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  2827. "%s: map_windows_tombstone_dn: Failed to map dn=\"%s\" "
  2828. "to windows tombstone dn.\n", agmt_get_long_name(prp->agmt),
  2829. slapi_entry_get_dn(e));
  2830. rc = 1;
  2831. }
  2832. slapi_ch_free_string(&cn);
  2833. slapi_ch_free_string(&guid);
  2834. return rc;
  2835. }
  2836. static int is_guid_dn(Slapi_DN *remote_dn)
  2837. {
  2838. if ((remote_dn != NULL) && (strncasecmp("<GUID=",
  2839. slapi_sdn_get_dn(remote_dn), 6) == 0)) {
  2840. return 1;
  2841. } else {
  2842. return 0;
  2843. }
  2844. }
  2845. static char*
  2846. extract_guid_from_entry(Slapi_Entry *e, int is_nt4)
  2847. {
  2848. char *guid = NULL;
  2849. Slapi_Value *val = NULL;
  2850. Slapi_Attr *attr = NULL;
  2851. slapi_entry_attr_find(e, "objectGUID", &attr);
  2852. if (attr)
  2853. {
  2854. slapi_attr_first_value(attr, &val);
  2855. if (val) {
  2856. if (is_nt4)
  2857. {
  2858. guid = slapi_ch_strdup(slapi_value_get_string(val));
  2859. } else
  2860. {
  2861. guid = convert_to_hex(val);
  2862. }
  2863. }
  2864. }
  2865. return guid;
  2866. }
  2867. #ifdef FOR_DEBUGGING
  2868. static void
  2869. extract_guid_from_entry_bv(Slapi_Entry *e, const struct berval **bv)
  2870. {
  2871. Slapi_Value *val = NULL;
  2872. Slapi_Attr *attr = NULL;
  2873. slapi_entry_attr_find(e, "objectGUID", &attr);
  2874. if (attr)
  2875. {
  2876. slapi_attr_first_value(attr, &val);
  2877. if (val) {
  2878. *bv = slapi_value_get_berval(val);
  2879. }
  2880. }
  2881. }
  2882. #endif
  2883. static char*
  2884. extract_username_from_entry(Slapi_Entry *e)
  2885. {
  2886. char *uid = NULL;
  2887. uid = slapi_entry_attr_get_charptr(e,"samAccountName");
  2888. return uid;
  2889. }
  2890. static char*
  2891. extract_ntuserdomainid_from_entry(Slapi_Entry *e)
  2892. {
  2893. char *uid = NULL;
  2894. uid = slapi_entry_attr_get_charptr(e,"ntuserdomainid");
  2895. return uid;
  2896. }
  2897. static Slapi_DN *make_dn_from_guid(char *guid, int is_nt4, const char* suffix)
  2898. {
  2899. Slapi_DN *new_dn = NULL;
  2900. char *dn_string = NULL;
  2901. if (guid)
  2902. {
  2903. if (is_nt4)
  2904. {
  2905. dn_string = PR_smprintf("GUID=%s,%s",guid,suffix);
  2906. } else
  2907. {
  2908. dn_string = PR_smprintf("<GUID=%s>",guid);
  2909. }
  2910. new_dn = slapi_sdn_new_dn_byval(dn_string);
  2911. PR_smprintf_free(dn_string);
  2912. }
  2913. /* dn string is now inside the Slapi_DN, and will be freed by its owner */
  2914. return new_dn;
  2915. }
  2916. static char*
  2917. extract_container(const Slapi_DN *entry_dn, const Slapi_DN *suffix_dn)
  2918. {
  2919. char *result = NULL;
  2920. /* First do a scope test to make sure that we weren't passed bogus arguments */
  2921. if (slapi_sdn_scope_test(entry_dn,suffix_dn,LDAP_SCOPE_SUBTREE))
  2922. {
  2923. Slapi_DN parent;
  2924. slapi_sdn_init(&parent);
  2925. /* Find the portion of the entry_dn between the RDN and the suffix */
  2926. /* Start with the parent of the entry DN */
  2927. slapi_sdn_get_parent(entry_dn, &parent);
  2928. /* Iterate finding the parent again until we have the suffix */
  2929. while (0 != slapi_sdn_compare(&parent,suffix_dn))
  2930. {
  2931. Slapi_DN child;
  2932. Slapi_RDN *rdn = slapi_rdn_new();
  2933. char *rdn_type = NULL;
  2934. char *rdn_str = NULL;
  2935. /* Append the current RDN to the new container string */
  2936. slapi_sdn_get_rdn(&parent,rdn);
  2937. slapi_rdn_get_first(rdn, &rdn_type, &rdn_str);
  2938. if (rdn_str)
  2939. {
  2940. result = PR_sprintf_append(result, "%s=%s,", rdn_type,rdn_str );
  2941. }
  2942. /* Don't free this until _after_ we've used the rdn_str */
  2943. slapi_rdn_free(&rdn);
  2944. /* Move to the next successive parent */
  2945. slapi_sdn_init(&child);
  2946. slapi_sdn_copy(&parent,&child);
  2947. slapi_sdn_done(&parent);
  2948. slapi_sdn_get_parent(&child, &parent);
  2949. slapi_sdn_done(&child);
  2950. }
  2951. slapi_sdn_done(&parent);
  2952. }
  2953. /* Always return something */
  2954. if (NULL == result)
  2955. {
  2956. result = slapi_ch_strdup("");
  2957. }
  2958. return result;
  2959. }
  2960. /* Given a non-tombstone entry, return the DN of its peer in AD (whether present or not) */
  2961. static int
  2962. map_entry_dn_outbound(Slapi_Entry *e, Slapi_DN **dn, Private_Repl_Protocol *prp, int *missing_entry, int guid_form)
  2963. {
  2964. int retval = 0;
  2965. char *guid = NULL;
  2966. Slapi_DN *new_dn = NULL;
  2967. int is_nt4 = windows_private_get_isnt4(prp->agmt);
  2968. const char *suffix = slapi_sdn_get_dn(windows_private_get_windows_subtree(prp->agmt));
  2969. /* To find the DN of the peer entry we first look for an ntUniqueId attribute
  2970. * on the local entry. If that's present, we generate a GUID-form DN.
  2971. * If there's no GUID, then we look for an ntUserDomainId attribute
  2972. * on the entry. If that's present we attempt to search for an entry with
  2973. * that samaccountName attribute value in AD. If we don't find any matching
  2974. * entry we generate a new DN using the entry's cn. If later, we find that
  2975. * this entry already exists, we handle that problem at the time. We don't
  2976. * check here. Note: for NT4 we always use ntUserDomainId for the samaccountname rdn, never cn.
  2977. */
  2978. *missing_entry = 0;
  2979. guid = slapi_entry_attr_get_charptr(e,"ntUniqueId");
  2980. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  2981. "%s: map_entry_dn_outbound: looking for AD entry for DS "
  2982. "dn=\"%s\" guid=\"%s\"\n",
  2983. agmt_get_long_name(prp->agmt),
  2984. slapi_entry_get_dn_const(e),
  2985. guid ? guid : "(null)");
  2986. if (guid && guid_form)
  2987. {
  2988. int rc = 0;
  2989. Slapi_Entry *remote_entry = NULL;
  2990. new_dn = make_dn_from_guid(guid, is_nt4, suffix);
  2991. slapi_ch_free_string(&guid);
  2992. /* There are certain cases where we will have a GUID, but the entry does not exist in
  2993. * AD. This happens when you delete an entry, then add it back elsewhere in the tree
  2994. * without removing the ntUniqueID attribute. We should verify that the entry really
  2995. * exists in AD. */
  2996. rc = windows_get_remote_entry(prp, new_dn, &remote_entry);
  2997. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  2998. "%s: map_entry_dn_outbound: return code %d from search "
  2999. "for AD entry dn=\"%s\" or dn=\"%s\"\n",
  3000. agmt_get_long_name(prp->agmt), rc,
  3001. slapi_sdn_get_dn(new_dn),
  3002. remote_entry ? slapi_entry_get_dn_const(remote_entry) : "(null)");
  3003. if (0 == rc && remote_entry) {
  3004. if (!is_subject_of_agreement_remote(remote_entry,prp->agmt)) {
  3005. /* The remote entry is our of scope of the agreement.
  3006. * Thus, we don't map the entry_dn.
  3007. * This occurs when the remote entry is moved out. */
  3008. slapi_sdn_free(&new_dn);
  3009. retval = -1;
  3010. }
  3011. slapi_entry_free(remote_entry);
  3012. } else {
  3013. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3014. "%s: map_entry_dn_outbound: entry not found - rc %d\n",
  3015. agmt_get_long_name(prp->agmt), rc);
  3016. /* We need to re-write the DN to a non-GUID DN if we're syncing to a
  3017. * Windows 2000 Server since tombstone reanimation is not supported.
  3018. * If we're syncing with Windows 2003 Server, we'll just use the GUID
  3019. * to reanimate the tombstone when processing the add operation. */
  3020. *missing_entry = 1;
  3021. if (!windows_private_get_iswin2k3(prp->agmt)) {
  3022. char *new_dn_string = NULL;
  3023. char *cn_string = NULL;
  3024. /* We can't use a GUID DN, so rewrite to the mapped DN. */
  3025. cn_string = slapi_entry_attr_get_charptr(e,"cn");
  3026. if (!cn_string) {
  3027. cn_string = slapi_entry_attr_get_charptr(e,"ntuserdomainid");
  3028. }
  3029. if (cn_string) {
  3030. char *container_str = NULL;
  3031. container_str = extract_container(slapi_entry_get_sdn_const(e),
  3032. windows_private_get_directory_subtree(prp->agmt));
  3033. new_dn_string = PR_smprintf("cn=%s,%s%s", cn_string, container_str, suffix);
  3034. if (new_dn_string) {
  3035. if (new_dn) {
  3036. slapi_sdn_free(&new_dn);
  3037. }
  3038. new_dn = slapi_sdn_new_dn_byval(new_dn_string);
  3039. PR_smprintf_free(new_dn_string);
  3040. }
  3041. slapi_ch_free_string(&cn_string);
  3042. slapi_ch_free_string(&container_str);
  3043. }
  3044. }
  3045. }
  3046. } else
  3047. {
  3048. /* No GUID found, try ntUserDomainId */
  3049. Slapi_Entry *remote_entry = NULL;
  3050. char *username = slapi_entry_attr_get_charptr(e,"ntUserDomainId");
  3051. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3052. "%s: map_entry_dn_outbound: looking for AD entry for DS "
  3053. "dn=\"%s\" username=\"%s\"\n",
  3054. agmt_get_long_name(prp->agmt),
  3055. slapi_entry_get_dn_const(e),
  3056. username ? username : "(null)");
  3057. if (username) {
  3058. retval = find_entry_by_attr_value_remote("samAccountName",username,&remote_entry,prp);
  3059. if (0 == retval && remote_entry)
  3060. {
  3061. /* Get the entry's DN */
  3062. new_dn = slapi_sdn_new();
  3063. slapi_sdn_copy(slapi_entry_get_sdn_const(remote_entry), new_dn);
  3064. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3065. "%s: map_entry_dn_outbound: found AD entry dn=\"%s\"\n",
  3066. agmt_get_long_name(prp->agmt),
  3067. slapi_sdn_get_dn(new_dn));
  3068. } else {
  3069. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3070. "%s: map_entry_dn_outbound: entry not found - rc %d\n",
  3071. agmt_get_long_name(prp->agmt), retval);
  3072. if (0 == retval)
  3073. {
  3074. char *new_dn_string = NULL;
  3075. char *cn_string = NULL;
  3076. *missing_entry = 1;
  3077. /* This means that we failed to find a peer entry */
  3078. /* In that case we need to generate the DN that we want to use */
  3079. /* Generated DN's take the form :
  3080. cn=<cn from local entry>, ... in the case that the local entry has a cn, OR
  3081. cn=<ntuserdomainid attribute value>, ... in the case that the local entry doesn't have a CN
  3082. */
  3083. if (is_nt4)
  3084. {
  3085. cn_string = slapi_entry_attr_get_charptr(e,"ntuserdomainid");
  3086. } else
  3087. {
  3088. cn_string = slapi_entry_attr_get_charptr(e,"cn");
  3089. if (!cn_string)
  3090. {
  3091. cn_string = slapi_entry_attr_get_charptr(e,"ntuserdomainid");
  3092. }
  3093. }
  3094. if (cn_string)
  3095. {
  3096. char *rdnstr = NULL;
  3097. char *container_str = NULL;
  3098. container_str = extract_container(slapi_entry_get_sdn_const(e), windows_private_get_directory_subtree(prp->agmt));
  3099. rdnstr = is_nt4 ? "samaccountname=%s,%s%s" : "cn=%s,%s%s";
  3100. new_dn_string = PR_smprintf(rdnstr,cn_string,container_str,suffix);
  3101. if (new_dn_string)
  3102. {
  3103. new_dn = slapi_sdn_new_dn_byval(new_dn_string);
  3104. PR_smprintf_free(new_dn_string);
  3105. }
  3106. slapi_ch_free_string(&cn_string);
  3107. slapi_ch_free_string(&container_str);
  3108. }
  3109. } else
  3110. {
  3111. /* This means that we failed to talk to the AD for some reason, the operation should be re-tried */
  3112. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  3113. "%s: map_entry_dn_outbound: failed to fetch entry from AD: dn=\"%s\", err=%d\n",
  3114. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)), retval);
  3115. retval = -1;
  3116. }
  3117. }
  3118. slapi_ch_free_string(&username);
  3119. }
  3120. if (remote_entry)
  3121. {
  3122. slapi_entry_free(remote_entry);
  3123. }
  3124. }
  3125. if (new_dn)
  3126. {
  3127. *dn = new_dn;
  3128. }
  3129. slapi_ch_free_string(&guid);
  3130. return retval;
  3131. }
  3132. /* Given a tombstone entry, return the DN of its peer in this server (if present) */
  3133. static int
  3134. map_tombstone_dn_inbound(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra)
  3135. {
  3136. int retval = 0;
  3137. Slapi_DN *new_dn = NULL;
  3138. char *guid = NULL;
  3139. const char *dn_string = NULL;
  3140. Slapi_Entry *matching_entry = NULL;
  3141. /* To map a tombstone's DN we need to first extract the entry's objectGUID from the DN
  3142. * CN=vpdxtAD_07\
  3143. DEL:d4ca4e16-e35b-400d-834a-f02db600f3fa,CN=Deleted Objects,DC=magpie,DC=com
  3144. */
  3145. *dn = NULL;
  3146. dn_string = slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)); /* This is a pointer from inside the sdn, no need to free */
  3147. guid = extract_guid_from_tombstone_dn(dn_string);
  3148. if (guid)
  3149. {
  3150. retval = find_entry_by_guid(guid,&matching_entry,ra);
  3151. if (retval)
  3152. {
  3153. if (ENTRY_NOTFOUND == retval)
  3154. {
  3155. } else
  3156. {
  3157. if (ENTRY_NOT_UNIQUE == retval)
  3158. {
  3159. } else
  3160. {
  3161. /* A real error */
  3162. }
  3163. }
  3164. } else
  3165. {
  3166. /* We found the matching entry : get its DN */
  3167. new_dn = slapi_sdn_dup(slapi_entry_get_sdn_const(matching_entry));
  3168. }
  3169. }
  3170. if (new_dn)
  3171. {
  3172. *dn = new_dn;
  3173. }
  3174. if (guid)
  3175. {
  3176. slapi_ch_free_string(&guid);
  3177. }
  3178. if (matching_entry)
  3179. {
  3180. slapi_entry_free(matching_entry);
  3181. }
  3182. return retval;
  3183. }
  3184. /* Given a non-tombstone entry, return the DN of its peer in this server (whether present or not) */
  3185. static int
  3186. map_entry_dn_inbound(Slapi_Entry *e, Slapi_DN **dn, const Repl_Agmt *ra)
  3187. {
  3188. int retval = 0;
  3189. Slapi_DN *new_dn = NULL;
  3190. char *guid = NULL;
  3191. char *username = NULL;
  3192. Slapi_Entry *matching_entry = NULL;
  3193. int is_user = 0;
  3194. int is_group = 0;
  3195. int is_nt4 = windows_private_get_isnt4(ra);
  3196. /* To map a non-tombstone's DN we need to first try to look it up by GUID.
  3197. * If we do not find it, then we need to generate the DN that it would have if added as a new entry.
  3198. */
  3199. *dn = NULL;
  3200. windows_is_remote_entry_user_or_group(e,&is_user,&is_group);
  3201. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3202. "%s: map_entry_dn_inbound: looking for local entry "
  3203. "matching AD entry [%s]\n",
  3204. agmt_get_long_name(ra),
  3205. slapi_entry_get_dn_const(e));
  3206. guid = extract_guid_from_entry(e, is_nt4);
  3207. if (guid)
  3208. {
  3209. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3210. "%s: map_entry_dn_inbound: looking for local entry "
  3211. "by guid [%s]\n",
  3212. agmt_get_long_name(ra),
  3213. guid);
  3214. retval = find_entry_by_guid(guid,&matching_entry,ra);
  3215. if (retval)
  3216. {
  3217. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3218. "%s: map_entry_dn_inbound: problem looking for guid: %d\n",
  3219. agmt_get_long_name(ra), retval);
  3220. if (ENTRY_NOTFOUND == retval)
  3221. {
  3222. } else
  3223. {
  3224. if (ENTRY_NOT_UNIQUE == retval)
  3225. {
  3226. } else
  3227. {
  3228. /* A real error */
  3229. goto error;
  3230. }
  3231. }
  3232. } else
  3233. {
  3234. /* We found the matching entry : get its DN */
  3235. new_dn = slapi_sdn_dup(slapi_entry_get_sdn_const(matching_entry));
  3236. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3237. "%s: map_entry_dn_inbound: found local entry [%s]\n",
  3238. agmt_get_long_name(ra),
  3239. slapi_sdn_get_dn(new_dn));
  3240. }
  3241. }
  3242. else
  3243. {
  3244. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3245. "%s: map_entry_dn_inbound: AD entry has no guid!\n",
  3246. agmt_get_long_name(ra));
  3247. }
  3248. /* If we failed to lookup by guid, try samaccountname */
  3249. if (NULL == new_dn)
  3250. {
  3251. username = extract_username_from_entry(e);
  3252. if (username) {
  3253. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3254. "%s: map_entry_dn_inbound: looking for local entry "
  3255. "by uid [%s]\n",
  3256. agmt_get_long_name(ra),
  3257. username);
  3258. retval = find_entry_by_username(username,&matching_entry,ra);
  3259. if (retval)
  3260. {
  3261. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3262. "%s: map_entry_dn_inbound: problem looking for username: %d\n",
  3263. agmt_get_long_name(ra), retval);
  3264. if (ENTRY_NOTFOUND == retval)
  3265. {
  3266. } else
  3267. {
  3268. if (ENTRY_NOT_UNIQUE == retval)
  3269. {
  3270. } else
  3271. {
  3272. /* A real error */
  3273. goto error;
  3274. }
  3275. }
  3276. } else
  3277. {
  3278. /* We found the matching entry : get its DN */
  3279. new_dn = slapi_sdn_dup(slapi_entry_get_sdn_const(matching_entry));
  3280. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3281. "%s: map_entry_dn_inbound: found local entry by name [%s]\n",
  3282. agmt_get_long_name(ra),
  3283. slapi_sdn_get_dn(new_dn));
  3284. }
  3285. }
  3286. else
  3287. {
  3288. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3289. "%s: map_entry_dn_inbound: AD entry has no username!\n",
  3290. agmt_get_long_name(ra));
  3291. }
  3292. }
  3293. /* If we couldn't find a matching entry by either method, then we need to invent a new DN */
  3294. if (NULL == new_dn)
  3295. {
  3296. /* The new DN has the form: uid=<samaccountname>,<sync'ed subtree> */
  3297. /* If an entry with this DN already exists, we fail and return no DN
  3298. * this is because we don't want to second-guess what the admin wants here:
  3299. * they may want to associate this existing entry with the peer AD entry,
  3300. * but if they intend that we say they must add the ntDomainUserId attribute to
  3301. * that entry.
  3302. */
  3303. char *new_dn_string = NULL;
  3304. if (username)
  3305. {
  3306. const char *suffix = slapi_sdn_get_dn(windows_private_get_directory_subtree(ra));
  3307. char *container_str = NULL;
  3308. container_str = extract_container(slapi_entry_get_sdn_const(e), windows_private_get_windows_subtree(ra));
  3309. /* Local DNs for users and groups are different */
  3310. if (is_user)
  3311. {
  3312. new_dn_string = PR_smprintf("uid=%s,%s%s",username,container_str,suffix);
  3313. winsync_plugin_call_get_new_ds_user_dn_cb(ra,
  3314. windows_private_get_raw_entry(ra),
  3315. e,
  3316. &new_dn_string,
  3317. windows_private_get_directory_subtree(ra),
  3318. windows_private_get_windows_subtree(ra));
  3319. } else
  3320. {
  3321. new_dn_string = PR_smprintf("cn=%s,%s%s",username,container_str,suffix);
  3322. if (is_group) {
  3323. winsync_plugin_call_get_new_ds_group_dn_cb(ra,
  3324. windows_private_get_raw_entry(ra),
  3325. e,
  3326. &new_dn_string,
  3327. windows_private_get_directory_subtree(ra),
  3328. windows_private_get_windows_subtree(ra));
  3329. }
  3330. }
  3331. new_dn = slapi_sdn_new_dn_byval(new_dn_string);
  3332. PR_smprintf_free(new_dn_string);
  3333. slapi_ch_free_string(&container_str);
  3334. /* Clear any earlier error */
  3335. retval = 0;
  3336. } else
  3337. {
  3338. /* Error, no username */
  3339. }
  3340. }
  3341. if (new_dn)
  3342. {
  3343. *dn = new_dn;
  3344. }
  3345. error:
  3346. if (guid)
  3347. {
  3348. PR_smprintf_free(guid);
  3349. }
  3350. if (matching_entry)
  3351. {
  3352. slapi_entry_free(matching_entry);
  3353. }
  3354. if (username)
  3355. {
  3356. slapi_ch_free_string(&username);
  3357. }
  3358. return retval;
  3359. }
  3360. /* Tests if the entry is subject to our agreement (i.e. is it in the sync'ed subtree in this server, and is it the right objectclass
  3361. * and does it have the right attribute values for sync ?)
  3362. */
  3363. static int
  3364. is_subject_of_agreement_local(const Slapi_Entry *local_entry, const Repl_Agmt *ra)
  3365. {
  3366. int retval = 0;
  3367. int is_in_subtree = 0;
  3368. const Slapi_DN *agreement_subtree = NULL;
  3369. /* First test for the sync'ed subtree */
  3370. agreement_subtree = windows_private_get_directory_subtree(ra);
  3371. if (NULL == agreement_subtree)
  3372. {
  3373. goto error;
  3374. }
  3375. is_in_subtree = slapi_sdn_scope_test(slapi_entry_get_sdn_const(local_entry), agreement_subtree, LDAP_SCOPE_SUBTREE);
  3376. if (is_in_subtree)
  3377. {
  3378. /* Next test for the correct kind of entry */
  3379. if (local_entry) {
  3380. if (slapi_filter_test_simple( (Slapi_Entry*)local_entry,
  3381. (Slapi_Filter*)windows_private_get_directory_filter(ra)) == 0)
  3382. {
  3383. retval = 1;
  3384. }
  3385. } else
  3386. {
  3387. /* Error: couldn't find the entry */
  3388. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  3389. "failed to find entry in is_subject_of_agreement_local: %d\n", retval);
  3390. retval = 0;
  3391. }
  3392. }
  3393. error:
  3394. return retval;
  3395. }
  3396. /* Tests if the entry is subject to our agreement (i.e. is it in the sync'ed subtree in AD and either a user or a group ?) */
  3397. static int
  3398. is_subject_of_agreement_remote(Slapi_Entry *e, const Repl_Agmt *ra)
  3399. {
  3400. int retval = 0;
  3401. int is_in_subtree = 0;
  3402. const Slapi_DN *agreement_subtree = NULL;
  3403. /* First test for the sync'ed subtree */
  3404. agreement_subtree = windows_private_get_windows_subtree(ra);
  3405. if (NULL == agreement_subtree)
  3406. {
  3407. goto error;
  3408. }
  3409. is_in_subtree = slapi_sdn_scope_test(slapi_entry_get_sdn_const(e), agreement_subtree, LDAP_SCOPE_SUBTREE);
  3410. if (is_in_subtree)
  3411. {
  3412. retval = 1;
  3413. }
  3414. error:
  3415. return retval;
  3416. }
  3417. static int
  3418. windows_create_local_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,const Slapi_DN* local_sdn)
  3419. {
  3420. int retval = 0;
  3421. char *entry_string = NULL;
  3422. Slapi_Entry *local_entry = NULL;
  3423. Slapi_PBlock* pb = NULL;
  3424. int is_user = 0;
  3425. int is_group = 0;
  3426. char *local_entry_template = NULL;
  3427. char *user_entry_template = NULL;
  3428. char *username = extract_username_from_entry(remote_entry);
  3429. Slapi_Attr *attr = NULL;
  3430. int rc = 0;
  3431. char *guid_str = NULL;
  3432. int is_nt4 = windows_private_get_isnt4(prp->agmt);
  3433. char *local_user_entry_template =
  3434. "dn: %s\n"
  3435. "objectclass:top\n"
  3436. "objectclass:person\n"
  3437. "objectclass:organizationalperson\n"
  3438. "objectclass:inetOrgPerson\n"
  3439. "objectclass:ntUser\n"
  3440. "ntUserDeleteAccount:true\n"
  3441. "uid:%s\n";
  3442. char *local_nt4_user_entry_template =
  3443. "dn: %s\n"
  3444. "objectclass:top\n"
  3445. "objectclass:person\n"
  3446. "objectclass:organizationalperson\n"
  3447. "objectclass:inetOrgPerson\n"
  3448. "objectclass:ntUser\n"
  3449. "ntUserDeleteAccount:true\n"
  3450. "uid:%s\n";
  3451. char *local_group_entry_template =
  3452. "dn: %s\n"
  3453. "objectclass:top\n"
  3454. "objectclass:groupofuniquenames\n"
  3455. "objectclass:ntGroup\n"
  3456. "ntGroupDeleteGroup:true\n"
  3457. "cn:%s\n";
  3458. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_create_local_entry\n", 0, 0, 0 );
  3459. windows_is_remote_entry_user_or_group(remote_entry,&is_user,&is_group);
  3460. user_entry_template = is_nt4 ? local_nt4_user_entry_template : local_user_entry_template;
  3461. local_entry_template = is_user ? user_entry_template : local_group_entry_template;
  3462. /* Create a new entry */
  3463. /* Give it its DN and username */
  3464. entry_string = slapi_ch_smprintf(local_entry_template,slapi_sdn_get_dn(local_sdn),username, username);
  3465. if (NULL == entry_string)
  3466. {
  3467. goto error;
  3468. }
  3469. local_entry = slapi_str2entry(entry_string, 0);
  3470. slapi_ch_free_string(&entry_string);
  3471. if (NULL == local_entry)
  3472. {
  3473. goto error;
  3474. }
  3475. /* Map the appropriate attributes sourced from the remote entry */
  3476. for (rc = slapi_entry_first_attr(remote_entry, &attr); rc == 0;
  3477. rc = slapi_entry_next_attr(remote_entry, attr, &attr))
  3478. {
  3479. char *type = NULL;
  3480. Slapi_ValueSet *vs = NULL;
  3481. int mapdn = 0;
  3482. slapi_attr_get_type( attr, &type );
  3483. slapi_attr_get_valueset(attr,&vs);
  3484. if ( is_straight_mapped_attr(type,is_user,is_nt4) )
  3485. {
  3486. /* copy over the attr values */
  3487. slapi_entry_add_valueset(local_entry,type,vs);
  3488. } else
  3489. {
  3490. char *new_type = NULL;
  3491. windows_map_attr_name(type , 0 /* from windows */, is_user, 1 /* create */, &new_type, &mapdn);
  3492. if (new_type)
  3493. {
  3494. if (mapdn)
  3495. {
  3496. Slapi_ValueSet *mapped_values = NULL;
  3497. map_dn_values(prp,vs,&mapped_values, 0 /* from windows */,0);
  3498. if (mapped_values)
  3499. {
  3500. slapi_entry_add_valueset(local_entry,new_type,mapped_values);
  3501. slapi_valueset_free(mapped_values);
  3502. mapped_values = NULL;
  3503. }
  3504. } else
  3505. {
  3506. slapi_entry_add_valueset(local_entry,new_type,vs);
  3507. }
  3508. slapi_ch_free_string(&new_type);
  3509. }
  3510. }
  3511. if (vs)
  3512. {
  3513. slapi_valueset_free(vs);
  3514. vs = NULL;
  3515. }
  3516. }
  3517. /* Copy over the GUID */
  3518. guid_str = extract_guid_from_entry(remote_entry, is_nt4);
  3519. if (guid_str)
  3520. {
  3521. slapi_entry_add_string(local_entry,"ntUniqueId",guid_str);
  3522. } else
  3523. {
  3524. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  3525. "extract_guid_from_entry entry %s failed to extract the guid\n", slapi_sdn_get_dn(local_sdn));
  3526. /* Fatal error : need the guid */
  3527. goto error;
  3528. }
  3529. /* Hack for NT4, which has no surname */
  3530. if (is_nt4 && is_user)
  3531. {
  3532. slapi_entry_add_string(local_entry,"sn",username);
  3533. }
  3534. if (is_user) {
  3535. winsync_plugin_call_pre_ds_add_user_cb(prp->agmt,
  3536. windows_private_get_raw_entry(prp->agmt),
  3537. remote_entry,
  3538. local_entry);
  3539. } else if (is_group) {
  3540. winsync_plugin_call_pre_ds_add_group_cb(prp->agmt,
  3541. windows_private_get_raw_entry(prp->agmt),
  3542. remote_entry,
  3543. local_entry);
  3544. }
  3545. /* Store it */
  3546. windows_dump_entry("Adding new local entry",local_entry);
  3547. pb = slapi_pblock_new();
  3548. slapi_add_entry_internal_set_pb(pb, local_entry, NULL,repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION),0);
  3549. slapi_add_internal_pb(pb);
  3550. local_entry = NULL; /* consumed by add */
  3551. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &retval);
  3552. if (retval) {
  3553. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  3554. "add operation of entry %s returned: %d\n", slapi_sdn_get_dn(local_sdn), retval);
  3555. }
  3556. error:
  3557. slapi_entry_free(local_entry);
  3558. slapi_ch_free_string(&guid_str);
  3559. if (pb)
  3560. {
  3561. slapi_pblock_destroy(pb);
  3562. }
  3563. if (username)
  3564. {
  3565. slapi_ch_free_string(&username);
  3566. }
  3567. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_create_local_entry\n", 0, 0, 0 );
  3568. return retval;
  3569. }
  3570. /* Function to generate the correct mods to bring 'local' attribute values into consistency with given 'remote' values */
  3571. /* 'local' and 'remote' in quotes because we actually use this function in both directions */
  3572. /* This function expects the value sets to have been already pruned to exclude values that are not
  3573. * subject to the agreement and present in the peer. */
  3574. static int
  3575. windows_generate_dn_value_mods(char *local_type, const Slapi_Attr *attr, Slapi_Mods *smods, Slapi_ValueSet *remote_values, Slapi_ValueSet *local_values, int *do_modify)
  3576. {
  3577. int ret = 0;
  3578. int i = 0;
  3579. Slapi_Value *rv = NULL;
  3580. Slapi_Value *lv = NULL;
  3581. /* We need to generate an ADD mod for each entry that is in the remote values but not in the local values */
  3582. /* Iterate over the remote values */
  3583. i = slapi_valueset_first_value(remote_values,&rv);
  3584. while (NULL != rv)
  3585. {
  3586. const char *remote_dn = slapi_value_get_string(rv);
  3587. int value_present_in_local_values = (NULL != slapi_valueset_find(attr, local_values, rv));
  3588. if (!value_present_in_local_values)
  3589. {
  3590. slapi_mods_add_string(smods,LDAP_MOD_ADD,local_type,remote_dn);
  3591. *do_modify = 1;
  3592. }
  3593. i = slapi_valueset_next_value(remote_values,i,&rv);
  3594. }
  3595. /* We need to generate a DEL mod for each entry that is in the local values but not in the remote values */
  3596. /* Iterate over the local values */
  3597. i = slapi_valueset_first_value(local_values,&lv);
  3598. while (NULL != lv)
  3599. {
  3600. const char *local_dn = slapi_value_get_string(lv);
  3601. int value_present_in_remote_values = (NULL != slapi_valueset_find(attr, remote_values, lv));
  3602. if (!value_present_in_remote_values)
  3603. {
  3604. slapi_mods_add_string(smods,LDAP_MOD_DELETE,local_type,local_dn);
  3605. *do_modify = 1;
  3606. }
  3607. i = slapi_valueset_next_value(local_values,i,&lv);
  3608. }
  3609. return ret;
  3610. }
  3611. /* Generate the mods for an update in either direction. Be careful... the "remote" entry is the DS entry in the to_windows case, but the AD entry in the other case. */
  3612. static int
  3613. windows_generate_update_mods(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,Slapi_Entry *local_entry, int to_windows, Slapi_Mods *smods, int *do_modify)
  3614. {
  3615. int retval = 0;
  3616. Slapi_Attr *attr = NULL;
  3617. Slapi_Attr *del_attr = NULL;
  3618. int is_user = 0;
  3619. int is_group = 0;
  3620. int rc = 0;
  3621. int is_nt4 = windows_private_get_isnt4(prp->agmt);
  3622. /* Iterate over the attributes on the remote entry, updating the local entry where appropriate */
  3623. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_generate_update_mods\n", 0, 0, 0 );
  3624. *do_modify = 0;
  3625. if (to_windows)
  3626. {
  3627. windows_is_local_entry_user_or_group(remote_entry,&is_user,&is_group);
  3628. } else
  3629. {
  3630. windows_is_remote_entry_user_or_group(remote_entry,&is_user,&is_group);
  3631. }
  3632. for (rc = slapi_entry_first_attr(remote_entry, &attr); rc == 0;
  3633. rc = slapi_entry_next_attr(remote_entry, attr, &attr))
  3634. {
  3635. int is_present_local = 0;
  3636. char *type = NULL;
  3637. Slapi_ValueSet *vs = NULL;
  3638. char *local_type = NULL;
  3639. Slapi_Attr *local_attr = NULL;
  3640. int is_guid = 0;
  3641. int mapdn = 0;
  3642. slapi_attr_get_type( attr, &type );
  3643. slapi_attr_get_valueset(attr,&vs);
  3644. /* First determine what we will do with this attr */
  3645. /* If it's a GUID, we need to take special action */
  3646. if (0 == slapi_attr_type_cmp(type,"objectGuid",SLAPI_TYPE_CMP_SUBTYPE) && !to_windows)
  3647. {
  3648. is_guid = 1;
  3649. local_type = slapi_ch_strdup("ntUniqueId");
  3650. } else
  3651. {
  3652. if ( is_straight_mapped_attr(type,is_user,is_nt4) ) {
  3653. local_type = slapi_ch_strdup(type);
  3654. } else {
  3655. windows_map_attr_name(type , to_windows, is_user, 0 /* not create */, &local_type, &mapdn);
  3656. }
  3657. is_guid = 0;
  3658. }
  3659. if (NULL == local_type)
  3660. {
  3661. /* Means we do not map this attribute */
  3662. if (vs)
  3663. {
  3664. slapi_valueset_free(vs);
  3665. vs = NULL;
  3666. }
  3667. continue;
  3668. }
  3669. if (to_windows && (0 == slapi_attr_type_cmp(local_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE))) {
  3670. slapi_entry_attr_find(local_entry,FAKE_STREET_ATTR_NAME,&local_attr);
  3671. } else {
  3672. slapi_entry_attr_find(local_entry,local_type,&local_attr);
  3673. }
  3674. is_present_local = (NULL == local_attr) ? 0 : 1;
  3675. /* Is the attribute present on the local entry ? */
  3676. if (is_present_local && !is_guid)
  3677. {
  3678. if (!mapdn)
  3679. {
  3680. int values_equal = 0;
  3681. /* We only have to deal with processing the cn here for
  3682. * operations coming from AD since the mapping for the
  3683. * to_windows case has the create only flag set. We
  3684. * just need to check if the value from the AD entry
  3685. * is already present in the DS entry. */
  3686. if (!to_windows && (0 == slapi_attr_type_cmp(type, "name", SLAPI_TYPE_CMP_SUBTYPE))) {
  3687. values_equal = attr_compare_present(attr, local_attr, 0);
  3688. /* AD has a length contraint on the initials attribute (in addition
  3689. * to defining it as single-valued), so treat is as a special case. */
  3690. } else if (0 == slapi_attr_type_cmp(type, "initials", SLAPI_TYPE_CMP_SUBTYPE)) {
  3691. values_equal = attr_compare_present(attr, local_attr, AD_INITIALS_LENGTH);
  3692. /* If this is a single valued type in AD, then we just check if the value
  3693. * in AD is present in our entry in DS. In this case, attr is from the AD
  3694. * entry, and local_attr is from the DS entry. */
  3695. } else if (!to_windows && is_single_valued_attr(type)) {
  3696. values_equal = attr_compare_present(attr, local_attr, 0);
  3697. /* If this is a single valued type in AD, then we just check if the AD
  3698. * entry already contains any value that is present in the DS entry. In
  3699. * this case, attr is from the DS entry, and local_attr is from the AD entry. */
  3700. } else if (to_windows && is_single_valued_attr(type)) {
  3701. values_equal = attr_compare_present(local_attr, attr, 0);
  3702. } else {
  3703. /* Compare the entire attribute values */
  3704. values_equal = attr_compare_equal(attr, local_attr, 0);
  3705. }
  3706. /* If it is then we need to replace the local values with the remote values if they are different */
  3707. if (!values_equal)
  3708. {
  3709. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  3710. "windows_generate_update_mods: %s, %s : values are different\n",
  3711. slapi_sdn_get_dn(slapi_entry_get_sdn_const(local_entry)), local_type);
  3712. if (to_windows && ((0 == slapi_attr_type_cmp(local_type, "streetAddress", SLAPI_TYPE_CMP_SUBTYPE)) ||
  3713. (0 == slapi_attr_type_cmp(local_type, "telephoneNumber", SLAPI_TYPE_CMP_SUBTYPE)) ||
  3714. (0 == slapi_attr_type_cmp(local_type, "physicalDeliveryOfficeName", SLAPI_TYPE_CMP_SUBTYPE)))) {
  3715. /* These attributes are single-valued in AD, so make
  3716. * sure we don't try to send more than one value. */
  3717. if (slapi_valueset_count(vs) > 1) {
  3718. int i = 0;
  3719. Slapi_Value *value = NULL;
  3720. Slapi_Value *new_value = NULL;
  3721. i = slapi_valueset_first_value(vs,&value);
  3722. if (i >= 0) {
  3723. /* Dup the first value, trash the valueset, then copy
  3724. * in the dup'd value. */
  3725. new_value = slapi_value_dup(value);
  3726. slapi_valueset_done(vs);
  3727. /* The below hands off the memory to the valueset */
  3728. slapi_valueset_add_value_ext(vs, new_value, SLAPI_VALUE_FLAG_PASSIN);
  3729. }
  3730. }
  3731. } else if ((0 == slapi_attr_type_cmp(local_type, "initials",
  3732. SLAPI_TYPE_CMP_SUBTYPE) && to_windows)) {
  3733. /* initials is constratined to a max length of
  3734. * 6 characters in AD, so trim the value if
  3735. * needed before sending. */
  3736. int i = 0;
  3737. const char *initials_value = NULL;
  3738. Slapi_Value *value = NULL;
  3739. i = slapi_valueset_first_value(vs,&value);
  3740. while (i >= 0) {
  3741. initials_value = slapi_value_get_string(value);
  3742. /* If > AD_INITIALS_LENGTH, trim the value */
  3743. if (strlen(initials_value) > AD_INITIALS_LENGTH) {
  3744. char *new_initials = PL_strndup(initials_value, AD_INITIALS_LENGTH);
  3745. /* the below hands off memory */
  3746. slapi_value_set_string_passin(value, new_initials);
  3747. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3748. "%s: windows_generate_update_mods: "
  3749. "Trimming initials attribute to %d characters.\n",
  3750. agmt_get_long_name(prp->agmt), AD_INITIALS_LENGTH);
  3751. }
  3752. i = slapi_valueset_next_value(vs, i, &value);
  3753. }
  3754. }
  3755. slapi_mods_add_mod_values(smods,LDAP_MOD_REPLACE,
  3756. local_type,valueset_get_valuearray(vs));
  3757. *do_modify = 1;
  3758. } else
  3759. {
  3760. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  3761. "windows_generate_update_mods: %s, %s : values are equal\n", slapi_sdn_get_dn(slapi_entry_get_sdn_const(local_entry)), local_type);
  3762. }
  3763. } else {
  3764. /* A dn-valued attribute : need to take special steps */
  3765. Slapi_ValueSet *mapped_remote_values = NULL;
  3766. /* First map all the DNs to that they're in a consistent domain */
  3767. map_dn_values(prp,vs,&mapped_remote_values, to_windows,0);
  3768. if (mapped_remote_values)
  3769. {
  3770. Slapi_ValueSet *local_values = NULL;
  3771. Slapi_ValueSet *restricted_local_values = NULL;
  3772. /* Now do a compare on the values, generating mods to bring them into consistency (if any) */
  3773. /* We ignore any DNs that are outside the scope of the agreement (on both sides) */
  3774. slapi_attr_get_valueset(local_attr,&local_values);
  3775. map_dn_values(prp,local_values,&restricted_local_values,!to_windows,1);
  3776. if (restricted_local_values)
  3777. {
  3778. windows_generate_dn_value_mods(local_type,local_attr,smods,mapped_remote_values,restricted_local_values,do_modify);
  3779. slapi_valueset_free(restricted_local_values);
  3780. restricted_local_values = NULL;
  3781. }
  3782. slapi_valueset_free(mapped_remote_values);
  3783. mapped_remote_values = NULL;
  3784. if (local_values)
  3785. {
  3786. slapi_valueset_free(local_values);
  3787. local_values = NULL;
  3788. }
  3789. }
  3790. }
  3791. } else
  3792. {
  3793. if (!is_present_local)
  3794. {
  3795. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  3796. "windows_generate_update_mods: %s, %s : values not present on peer entry\n", slapi_sdn_get_dn(slapi_entry_get_sdn_const(local_entry)), local_type);
  3797. /* If it is currently absent, then we add the value from the remote entry */
  3798. if (is_guid)
  3799. {
  3800. /* Translate the guid value */
  3801. char *guid = extract_guid_from_entry(remote_entry, is_nt4);
  3802. if (guid)
  3803. {
  3804. slapi_mods_add_string(smods,LDAP_MOD_ADD,local_type,guid);
  3805. slapi_ch_free_string(&guid);
  3806. }
  3807. } else
  3808. {
  3809. /* Handle DN valued attributes here */
  3810. if (mapdn)
  3811. {
  3812. Slapi_ValueSet *mapped_values = NULL;
  3813. map_dn_values(prp,vs,&mapped_values, to_windows,0);
  3814. if (mapped_values)
  3815. {
  3816. slapi_mods_add_mod_values(smods,LDAP_MOD_ADD,local_type,valueset_get_valuearray(mapped_values));
  3817. slapi_valueset_free(mapped_values);
  3818. mapped_values = NULL;
  3819. }
  3820. } else
  3821. {
  3822. /* If this attribute is single-valued in AD,
  3823. * we only want to send the first value. */
  3824. if (to_windows && is_single_valued_attr(local_type)) {
  3825. if (slapi_valueset_count(vs) > 1) {
  3826. int i = 0;
  3827. Slapi_Value *value = NULL;
  3828. Slapi_Value *new_value = NULL;
  3829. i = slapi_valueset_first_value(vs,&value);
  3830. if (i >= 0) {
  3831. /* Dup the first value, trash the valueset, then copy
  3832. * in the dup'd value. */
  3833. new_value = slapi_value_dup(value);
  3834. slapi_valueset_done(vs);
  3835. /* The below hands off the memory to the valueset */
  3836. slapi_valueset_add_value_ext(vs, new_value, SLAPI_VALUE_FLAG_PASSIN);
  3837. }
  3838. }
  3839. }
  3840. if (to_windows && (0 == slapi_attr_type_cmp(local_type, "initials",
  3841. SLAPI_TYPE_CMP_SUBTYPE))) {
  3842. /* initials is constratined to a max length of
  3843. * 6 characters in AD, so trim the value if
  3844. * needed before sending. */
  3845. int i = 0;
  3846. const char *initials_value = NULL;
  3847. Slapi_Value *value = NULL;
  3848. i = slapi_valueset_first_value(vs,&value);
  3849. while (i >= 0) {
  3850. initials_value = slapi_value_get_string(value);
  3851. /* If > AD_INITIALS_LENGTH, trim the value */
  3852. if (strlen(initials_value) > AD_INITIALS_LENGTH) {
  3853. char *new_initials = PL_strndup(initials_value, AD_INITIALS_LENGTH);
  3854. /* the below hands off memory */
  3855. slapi_value_set_string_passin(value, new_initials);
  3856. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  3857. "%s: windows_generate_update_mods: "
  3858. "Trimming initials attribute to %d characters.\n",
  3859. agmt_get_long_name(prp->agmt), AD_INITIALS_LENGTH);
  3860. }
  3861. i = slapi_valueset_next_value(vs, i, &value);
  3862. }
  3863. }
  3864. slapi_mods_add_mod_values(smods,LDAP_MOD_ADD,local_type,valueset_get_valuearray(vs));
  3865. }
  3866. }
  3867. /* Only set the do_modify flag if smods is not empty */
  3868. if (slapi_mods_get_num_mods(smods) > 0) {
  3869. *do_modify = 1;
  3870. }
  3871. }
  3872. }
  3873. if (vs)
  3874. {
  3875. slapi_valueset_free(vs);
  3876. vs = NULL;
  3877. }
  3878. slapi_ch_free_string(&local_type);
  3879. }
  3880. /* Check if any attributes were deleted from the remote entry */
  3881. entry_first_deleted_attribute(remote_entry, &del_attr);
  3882. while (del_attr != NULL) {
  3883. Slapi_Attr *local_attr = NULL;
  3884. char *type = NULL;
  3885. char *local_type = NULL;
  3886. int mapdn = 0;
  3887. /* Map remote type to local type */
  3888. slapi_attr_get_type(del_attr, &type);
  3889. if ( is_straight_mapped_attr(type,is_user,is_nt4) ) {
  3890. local_type = slapi_ch_strdup(type);
  3891. } else {
  3892. windows_map_attr_name(type , to_windows, is_user, 0 /* not create */, &local_type, &mapdn);
  3893. }
  3894. /* Check if this attr exists in the local entry */
  3895. if (local_type) {
  3896. slapi_entry_attr_find(local_entry, local_type, &local_attr);
  3897. if (local_attr) {
  3898. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  3899. "windows_generate_update_mods: deleting %s attribute from local entry\n", local_type);
  3900. /* Delete this attr from the local entry */
  3901. slapi_mods_add_mod_values(smods, LDAP_MOD_DELETE, local_type, NULL);
  3902. *do_modify = 1;
  3903. }
  3904. }
  3905. entry_next_deleted_attribute(remote_entry, &del_attr);
  3906. slapi_ch_free_string(&local_type);
  3907. }
  3908. if (to_windows) {
  3909. if (is_user) {
  3910. winsync_plugin_call_pre_ad_mod_user_cb(prp->agmt,
  3911. windows_private_get_raw_entry(prp->agmt),
  3912. local_entry, /* the cooked ad entry */
  3913. remote_entry, /* the ds entry */
  3914. smods,
  3915. do_modify);
  3916. } else if (is_group) {
  3917. winsync_plugin_call_pre_ad_mod_group_cb(prp->agmt,
  3918. windows_private_get_raw_entry(prp->agmt),
  3919. local_entry, /* the cooked ad entry */
  3920. remote_entry, /* the ds entry */
  3921. smods,
  3922. do_modify);
  3923. }
  3924. } else {
  3925. if (is_user) {
  3926. winsync_plugin_call_pre_ds_mod_user_cb(prp->agmt,
  3927. windows_private_get_raw_entry(prp->agmt),
  3928. remote_entry, /* the cooked ad entry */
  3929. local_entry, /* the ds entry */
  3930. smods,
  3931. do_modify);
  3932. } else if (is_group) {
  3933. winsync_plugin_call_pre_ds_mod_group_cb(prp->agmt,
  3934. windows_private_get_raw_entry(prp->agmt),
  3935. remote_entry, /* the cooked ad entry */
  3936. local_entry, /* the ds entry */
  3937. smods,
  3938. do_modify);
  3939. }
  3940. }
  3941. if (slapi_is_loglevel_set(SLAPI_LOG_REPL) && *do_modify)
  3942. {
  3943. slapi_mods_dump(smods,"windows sync");
  3944. }
  3945. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_generate_update_mods: %d\n", retval, 0, 0 );
  3946. return retval;
  3947. }
  3948. static int
  3949. windows_update_remote_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,Slapi_Entry *local_entry)
  3950. {
  3951. Slapi_Mods smods = {0};
  3952. int retval = 0;
  3953. int do_modify = 0;
  3954. slapi_mods_init (&smods, 0);
  3955. retval = windows_generate_update_mods(prp,local_entry,remote_entry,1,&smods,&do_modify);
  3956. /* Now perform the modify if we need to */
  3957. if (0 == retval && do_modify)
  3958. {
  3959. char dnbuf[BUFSIZ];
  3960. const char *dn = slapi_sdn_get_dn(slapi_entry_get_sdn_const(remote_entry));
  3961. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  3962. "windows_update_remote_entry: modifying entry %s\n", escape_string(dn, dnbuf));
  3963. retval = windows_conn_send_modify(prp->conn, slapi_sdn_get_dn(slapi_entry_get_sdn_const(remote_entry)),slapi_mods_get_ldapmods_byref(&smods), NULL,NULL);
  3964. } else
  3965. {
  3966. char dnbuf[BUFSIZ];
  3967. const char *dn = slapi_sdn_get_dn(slapi_entry_get_sdn_const(remote_entry));
  3968. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  3969. "no mods generated for remote entry: %s\n", escape_string(dn, dnbuf));
  3970. }
  3971. slapi_mods_done(&smods);
  3972. return retval;
  3973. }
  3974. static int
  3975. windows_update_local_entry(Private_Repl_Protocol *prp,Slapi_Entry *remote_entry,Slapi_Entry *local_entry)
  3976. {
  3977. Slapi_Mods smods = {0};
  3978. int retval = 0;
  3979. Slapi_PBlock *pb = NULL;
  3980. int do_modify = 0;
  3981. char *newsuperior = NULL;
  3982. const char *newrdn = NULL;
  3983. int is_user = 0, is_group = 0;
  3984. const char *newdn = NULL;
  3985. Slapi_RDN rdn = {0};
  3986. windows_is_local_entry_user_or_group(local_entry, &is_user, &is_group);
  3987. /* Compare the local and remote RDNs if it is a group */
  3988. /* If they don't match, set it to newrdn. */
  3989. if (is_group && strcmp(slapi_sdn_get_ndn(slapi_entry_get_sdn(local_entry)),
  3990. slapi_sdn_get_ndn(slapi_entry_get_sdn(remote_entry)))) {
  3991. newdn = slapi_sdn_get_dn(slapi_entry_get_sdn(remote_entry));
  3992. slapi_rdn_set_dn(&rdn, newdn);
  3993. newrdn = slapi_rdn_get_rdn(&rdn);
  3994. }
  3995. /* compare the parents */
  3996. retval = windows_get_superior_change(prp,
  3997. slapi_entry_get_sdn(local_entry),
  3998. slapi_entry_get_sdn(remote_entry),
  3999. &newsuperior, 0 /* to_windows */);
  4000. if (newsuperior || newrdn) {
  4001. /* remote parent is different from the local parent */
  4002. Slapi_PBlock *pb = slapi_pblock_new ();
  4003. if (NULL == newrdn) {
  4004. newdn = slapi_entry_get_dn_const(local_entry);
  4005. slapi_rdn_set_dn(&rdn, newdn);
  4006. newrdn = slapi_rdn_get_rdn(&rdn);
  4007. }
  4008. /* rename entry */
  4009. slapi_rename_internal_set_pb (pb,
  4010. slapi_sdn_get_dn(slapi_entry_get_sdn(local_entry)),
  4011. newrdn, newsuperior, 1 /* delete old RDNS */,
  4012. NULL /* controls */, NULL /* uniqueid */,
  4013. repl_get_plugin_identity(PLUGIN_MULTIMASTER_REPLICATION), 0);
  4014. slapi_modrdn_internal_pb (pb);
  4015. slapi_pblock_get(pb, SLAPI_PLUGIN_INTOP_RESULT, &retval);
  4016. slapi_pblock_destroy (pb);
  4017. slapi_ch_free_string(&newsuperior);
  4018. slapi_rdn_done(&rdn);
  4019. if (LDAP_SUCCESS != retval) {
  4020. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  4021. "failed to rename entry (%s); "
  4022. "LDAP error - %d\n", newdn, retval);
  4023. return retval;
  4024. }
  4025. }
  4026. slapi_mods_init (&smods, 0);
  4027. retval = windows_generate_update_mods(prp,remote_entry,local_entry,0,&smods,&do_modify);
  4028. /* Now perform the modify if we need to */
  4029. if (0 == retval && do_modify)
  4030. {
  4031. int rc = 0;
  4032. pb = slapi_pblock_new();
  4033. if (pb)
  4034. {
  4035. char dnbuf[BUFSIZ];
  4036. const char *dn = slapi_sdn_get_dn(slapi_entry_get_sdn_const(local_entry));
  4037. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  4038. "modifying entry: %s\n", escape_string(dn, dnbuf));
  4039. slapi_modify_internal_set_pb (pb, slapi_entry_get_ndn(local_entry), slapi_mods_get_ldapmods_byref(&smods), NULL, NULL,
  4040. repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
  4041. slapi_modify_internal_pb (pb);
  4042. slapi_pblock_get (pb, SLAPI_PLUGIN_INTOP_RESULT, &rc);
  4043. if (rc)
  4044. {
  4045. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  4046. "windows_update_local_entry: failed to modify entry %s\n", escape_string(dn, dnbuf));
  4047. }
  4048. slapi_pblock_destroy(pb);
  4049. } else
  4050. {
  4051. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  4052. "failed to make pb in windows_update_local_entry\n");
  4053. }
  4054. } else
  4055. {
  4056. char dnbuf[BUFSIZ];
  4057. const char *dn = slapi_sdn_get_dn(slapi_entry_get_sdn_const(local_entry));
  4058. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  4059. "no mods generated for local entry: %s\n", escape_string(dn, dnbuf));
  4060. }
  4061. slapi_mods_done(&smods);
  4062. return retval;
  4063. }
  4064. static int
  4065. windows_process_total_add(Private_Repl_Protocol *prp,Slapi_Entry *e, Slapi_DN* remote_dn, int missing_entry)
  4066. {
  4067. int retval = 0;
  4068. LDAPMod **entryattrs = NULL;
  4069. Slapi_Entry *mapped_entry = NULL;
  4070. char *password = NULL;
  4071. const Slapi_DN* local_dn = NULL;
  4072. int can_add = winsync_plugin_call_can_add_entry_to_ad_cb(prp->agmt, e, remote_dn);
  4073. /* First map the entry */
  4074. local_dn = slapi_entry_get_sdn_const(e);
  4075. if (missing_entry) {
  4076. if (can_add) {
  4077. retval = windows_create_remote_entry(prp, e, remote_dn, &mapped_entry, &password);
  4078. } else {
  4079. return retval; /* cannot add and no entry to modify */
  4080. }
  4081. }
  4082. /* Convert entry to mods */
  4083. if (0 == retval && mapped_entry)
  4084. {
  4085. (void)slapi_entry2mods (mapped_entry , NULL /* &entrydn : We don't need it */, &entryattrs);
  4086. slapi_entry_free(mapped_entry);
  4087. mapped_entry = NULL;
  4088. if (NULL == entryattrs)
  4089. {
  4090. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_replay_update: Cannot convert entry to LDAPMods.\n",agmt_get_long_name(prp->agmt));
  4091. retval = CONN_LOCAL_ERROR;
  4092. }
  4093. else
  4094. {
  4095. windows_log_add_entry_remote(local_dn, remote_dn);
  4096. retval = windows_conn_send_add(prp->conn, slapi_sdn_get_dn(remote_dn), entryattrs, NULL, NULL /* returned controls */);
  4097. /* It's possible that the entry already exists in AD, in which case we fall back to modify it */
  4098. if (retval)
  4099. {
  4100. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_replay_update: Cannot replay add operation.\n",agmt_get_long_name(prp->agmt));
  4101. }
  4102. ldap_mods_free(entryattrs, 1);
  4103. entryattrs = NULL;
  4104. if (retval == 0) { /* set the account control bits */
  4105. retval = send_accountcontrol_modify(remote_dn, prp, missing_entry);
  4106. }
  4107. }
  4108. } else
  4109. {
  4110. /* Entry already exists, need to mod it instead */
  4111. Slapi_Entry *remote_entry = NULL;
  4112. /* Get the remote entry */
  4113. retval = windows_get_remote_entry(prp, remote_dn,&remote_entry);
  4114. if (0 == retval && remote_entry)
  4115. {
  4116. retval = windows_update_remote_entry(prp,remote_entry,e);
  4117. /* Detect the case where the error is benign */
  4118. if (retval)
  4119. {
  4120. int operation = 0;
  4121. int error = 0;
  4122. conn_get_error(prp->conn, &operation, &error);
  4123. if (windows_ignore_error_and_keep_going(error))
  4124. {
  4125. retval = CONN_OPERATION_SUCCESS;
  4126. }
  4127. }
  4128. }
  4129. if (remote_entry)
  4130. {
  4131. slapi_entry_free(remote_entry);
  4132. }
  4133. }
  4134. slapi_ch_free_string(&password);
  4135. return retval;
  4136. }
  4137. /* Entry point for the total protocol */
  4138. int windows_process_total_entry(Private_Repl_Protocol *prp,Slapi_Entry *e)
  4139. {
  4140. int retval = 0;
  4141. int is_ours = 0;
  4142. Slapi_DN *remote_dn = NULL;
  4143. int missing_entry = 0;
  4144. const Slapi_DN *local_dn = slapi_entry_get_sdn_const(e);
  4145. /* First check if the entry is for us */
  4146. is_ours = is_subject_of_agreement_local(e, prp->agmt);
  4147. slapi_log_error(SLAPI_LOG_REPL, repl_plugin_name,
  4148. "%s: windows_process_total_entry: Looking dn=\"%s\" (%s)\n",
  4149. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)), is_ours ? "ours" : "not ours");
  4150. if (is_ours)
  4151. {
  4152. retval = map_entry_dn_outbound(e,&remote_dn,prp,&missing_entry,0 /* we don't want the GUID */);
  4153. if (retval || NULL == remote_dn)
  4154. {
  4155. slapi_log_error(SLAPI_LOG_FATAL, repl_plugin_name,
  4156. "%s: windows_replay_update: failed map dn for total update dn=\"%s\"\n",
  4157. agmt_get_long_name(prp->agmt), slapi_sdn_get_dn(local_dn));
  4158. goto error;
  4159. }
  4160. retval = windows_process_total_add(prp,e,remote_dn,missing_entry);
  4161. }
  4162. if (remote_dn)
  4163. {
  4164. slapi_sdn_free(&remote_dn);
  4165. }
  4166. error:
  4167. return retval;
  4168. }
  4169. static int
  4170. windows_search_local_entry_by_uniqueid(Private_Repl_Protocol *prp, const char *uniqueid, char ** attrs, Slapi_Entry **ret_entry, int tombstone, void * component_identity, int is_global)
  4171. {
  4172. Slapi_Entry **entries = NULL;
  4173. Slapi_PBlock *int_search_pb = NULL;
  4174. int rc = 0;
  4175. char *filter_string = NULL;
  4176. const Slapi_DN *local_subtree = NULL;
  4177. *ret_entry = NULL;
  4178. if (is_global) { /* Search from the suffix (rename case) */
  4179. local_subtree = agmt_get_replarea(prp->agmt);
  4180. } else {
  4181. local_subtree = windows_private_get_directory_subtree(prp->agmt);
  4182. }
  4183. /* Searching for tombstones can be expensive, so the caller needs to specify if
  4184. * we should search for a tombstone entry, or a normal entry. */
  4185. if (tombstone) {
  4186. filter_string = PR_smprintf("(&(objectclass=nsTombstone)(nsUniqueid=%s))", uniqueid);
  4187. } else {
  4188. filter_string = PR_smprintf("(&(|(objectclass=*)(objectclass=ldapsubentry))(nsUniqueid=%s))",uniqueid);
  4189. }
  4190. int_search_pb = slapi_pblock_new ();
  4191. slapi_search_internal_set_pb ( int_search_pb, slapi_sdn_get_dn(local_subtree), LDAP_SCOPE_SUBTREE, filter_string,
  4192. attrs ,
  4193. 0 /* attrsonly */, NULL /* controls */,
  4194. NULL /* uniqueid */,
  4195. component_identity, 0 /* actions */ );
  4196. slapi_search_internal_pb ( int_search_pb );
  4197. slapi_pblock_get( int_search_pb, SLAPI_PLUGIN_INTOP_RESULT, &rc );
  4198. if ( LDAP_SUCCESS == rc ) {
  4199. slapi_pblock_get( int_search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES, &entries );
  4200. if ( NULL != entries && NULL != entries[ 0 ]) {
  4201. Slapi_Entry *temp_entry = NULL;
  4202. temp_entry = entries[ 0 ];
  4203. *ret_entry = slapi_entry_dup(temp_entry);
  4204. } else {
  4205. /* No entry there */
  4206. rc = LDAP_NO_SUCH_OBJECT;
  4207. }
  4208. }
  4209. slapi_free_search_results_internal(int_search_pb);
  4210. slapi_pblock_destroy(int_search_pb);
  4211. int_search_pb = NULL;
  4212. if (filter_string)
  4213. {
  4214. PR_smprintf_free(filter_string);
  4215. }
  4216. return rc;
  4217. }
  4218. static int
  4219. windows_get_local_entry_by_uniqueid(Private_Repl_Protocol *prp,const char* uniqueid,Slapi_Entry **local_entry, int is_global)
  4220. {
  4221. int retval = ENTRY_NOTFOUND;
  4222. Slapi_Entry *new_entry = NULL;
  4223. windows_search_local_entry_by_uniqueid( prp, uniqueid, NULL, &new_entry, 0, /* Don't search tombstones */
  4224. repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), is_global);
  4225. if (new_entry)
  4226. {
  4227. *local_entry = new_entry;
  4228. retval = 0;
  4229. }
  4230. return retval;
  4231. }
  4232. static int
  4233. windows_get_local_tombstone_by_uniqueid(Private_Repl_Protocol *prp,const char* uniqueid,Slapi_Entry **local_entry)
  4234. {
  4235. int retval = ENTRY_NOTFOUND;
  4236. Slapi_Entry *new_entry = NULL;
  4237. windows_search_local_entry_by_uniqueid( prp, uniqueid, NULL, &new_entry, 1, /* Search for tombstones */
  4238. repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION), 0);
  4239. if (new_entry)
  4240. {
  4241. *local_entry = new_entry;
  4242. retval = 0;
  4243. }
  4244. return retval;
  4245. }
  4246. static int
  4247. windows_get_local_entry(const Slapi_DN* local_dn,Slapi_Entry **local_entry)
  4248. {
  4249. int retval = ENTRY_NOTFOUND;
  4250. Slapi_Entry *new_entry = NULL;
  4251. slapi_search_internal_get_entry( (Slapi_DN*)local_dn, NULL, &new_entry,
  4252. repl_get_plugin_identity (PLUGIN_MULTIMASTER_REPLICATION));
  4253. if (new_entry)
  4254. {
  4255. *local_entry = new_entry;
  4256. retval = 0;
  4257. }
  4258. return retval;
  4259. }
  4260. static int
  4261. windows_process_dirsync_entry(Private_Repl_Protocol *prp,Slapi_Entry *e, int is_total)
  4262. {
  4263. Slapi_DN* local_sdn = NULL;
  4264. int rc = 0;
  4265. int retried = 0;
  4266. Slapi_Entry *found_entry = NULL;
  4267. /* deleted users are outside the 'correct container'.
  4268. They live in cn=deleted objects, windows_private_get_directory_subtree( prp->agmt) */
  4269. if (is_tombstone(prp, e))
  4270. {
  4271. rc = map_tombstone_dn_inbound(e, &local_sdn, prp->agmt);
  4272. if ((0 == rc) && local_sdn)
  4273. {
  4274. /* Go ahead and delte the local peer */
  4275. rc = windows_delete_local_entry(local_sdn);
  4276. slapi_sdn_free(&local_sdn);
  4277. } else
  4278. {
  4279. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,"%s: windows_process_dirsync_entry: failed to map tombstone dn.\n",agmt_get_long_name(prp->agmt));
  4280. }
  4281. } else
  4282. {
  4283. /* Is this entry one we should be interested in ? */
  4284. if (is_subject_of_agreement_remote(e,prp->agmt))
  4285. {
  4286. retry:
  4287. /* First make its local DN */
  4288. rc = map_entry_dn_inbound(e, &local_sdn, prp->agmt);
  4289. if ((0 == rc) && local_sdn)
  4290. {
  4291. Slapi_Entry *local_entry = NULL;
  4292. /* Get the local entry if it exists */
  4293. rc = windows_get_local_entry(local_sdn,&local_entry);
  4294. if ((0 == rc) && local_entry)
  4295. {
  4296. /* Since the entry exists we should now make it match the entry we received from AD */
  4297. /* Actually we are better off simply fetching the entire entry from AD and using that
  4298. * because otherwise we don't get all the attributes we need to make sense of it such as
  4299. * objectclass */
  4300. Slapi_Entry *remote_entry = NULL;
  4301. windows_get_remote_entry(prp,slapi_entry_get_sdn_const(e),&remote_entry);
  4302. if (remote_entry)
  4303. {
  4304. /* We need to check for any deleted attrs from the dirsync entry
  4305. * and pass them into the newly fetched remote entry. */
  4306. Slapi_Attr *attr = NULL;
  4307. Slapi_Attr *rem_attr = NULL;
  4308. entry_first_deleted_attribute(e, &attr);
  4309. while (attr != NULL) {
  4310. /* We need to dup the attr and add it to the remote entry.
  4311. * rem_attr is now owned by remote_entry, so don't free it */
  4312. rem_attr = slapi_attr_dup(attr);
  4313. if (rem_attr) {
  4314. entry_add_deleted_attribute_wsi(remote_entry, rem_attr);
  4315. rem_attr = NULL;
  4316. }
  4317. entry_next_deleted_attribute(e, &attr);
  4318. }
  4319. rc = windows_update_local_entry(prp, remote_entry, local_entry);
  4320. slapi_entry_free(remote_entry);
  4321. remote_entry = NULL;
  4322. } else
  4323. {
  4324. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,"%s: windows_process_dirsync_entry: failed to fetch inbound entry.\n",agmt_get_long_name(prp->agmt));
  4325. }
  4326. slapi_entry_free(local_entry);
  4327. if (rc) {
  4328. /* Something bad happened */
  4329. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,"%s: windows_process_dirsync_entry: failed to update inbound entry for %s.\n",agmt_get_long_name(prp->agmt),
  4330. slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)));
  4331. }
  4332. } else
  4333. {
  4334. /* If it doesn't exist, try to make it */
  4335. if (add_local_entry_allowed(prp,e))
  4336. {
  4337. windows_create_local_entry(prp,e,local_sdn);
  4338. } else
  4339. {
  4340. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,"%s: windows_process_dirsync_entry: not allowed to add entry %s.\n",agmt_get_long_name(prp->agmt)
  4341. , slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)));
  4342. }
  4343. }
  4344. slapi_sdn_free(&local_sdn);
  4345. } else if (0 == retried) {
  4346. /* We should try one more thing. */
  4347. /* In case a remote entry is moved from the outside of scope of
  4348. * the agreement into the inside, the entry e only has
  4349. * attributes: parentguid, objectguid, instancetype, name.
  4350. * We search Windows with the dn and retry using the found
  4351. * entry.
  4352. */
  4353. ConnResult cres = 0;
  4354. const char *searchbase = slapi_entry_get_dn_const(e);
  4355. char *filter = "(objectclass=*)";
  4356. retried = 1;
  4357. cres = windows_search_entry(prp->conn, (char*)searchbase,
  4358. filter, &found_entry);
  4359. if (0 == cres && found_entry) {
  4360. /*
  4361. * Entry e originally allocated in windows_dirsync_inc_run
  4362. * is freed in windows_dirsync_inc_run. Assigning
  4363. * found_entry to e does not break the logic.
  4364. * "found_entry" is freed at the end of this function.
  4365. */
  4366. e = found_entry;
  4367. goto retry;
  4368. }
  4369. } else {
  4370. /* We should have been able to map the DN, so this is an error */
  4371. slapi_log_error(SLAPI_LOG_REPL, windows_repl_plugin_name,
  4372. "%s: windows_process_dirsync_entry: failed to map "
  4373. "inbound entry %s - rc is %d dn is [%s].\n",
  4374. agmt_get_long_name(prp->agmt),
  4375. slapi_sdn_get_dn(slapi_entry_get_sdn_const(e)),
  4376. rc,
  4377. local_sdn ? slapi_sdn_get_dn(local_sdn) : "null");
  4378. }
  4379. } /* subject of agreement */
  4380. else { /* The remote entry might be moved out of scope of agreement. */
  4381. /* First make its local DN */
  4382. rc = map_entry_dn_inbound(e, &local_sdn, prp->agmt);
  4383. if ((0 == rc) && local_sdn)
  4384. {
  4385. Slapi_Entry *local_entry = NULL;
  4386. /* Get the local entry if it exists */
  4387. rc = windows_get_local_entry(local_sdn, &local_entry);
  4388. if ((0 == rc) && local_entry)
  4389. {
  4390. /* Need to delete the local entry since the remote counter
  4391. * part is now moved out of scope of the agreement. */
  4392. /* Since map_Entry_dn_oubound returned local_sdn,
  4393. * the entry is either user or group. */
  4394. rc = windows_delete_local_entry(local_sdn);
  4395. slapi_entry_free(local_entry);
  4396. }
  4397. slapi_sdn_free(&local_sdn);
  4398. }
  4399. }
  4400. } /* is tombstone */
  4401. slapi_entry_free(found_entry);
  4402. return rc;
  4403. }
  4404. void
  4405. windows_dirsync_inc_run(Private_Repl_Protocol *prp)
  4406. {
  4407. int rc = 0;
  4408. int done = 0;
  4409. LDAPDebug( LDAP_DEBUG_TRACE, "=> windows_dirsync_inc_run\n", 0, 0, 0 );
  4410. while (!done) {
  4411. Slapi_Entry *e = NULL;
  4412. rc = send_dirsync_search(prp->conn);
  4413. if (rc != CONN_OPERATION_SUCCESS)
  4414. {
  4415. slapi_log_error(SLAPI_LOG_FATAL, windows_repl_plugin_name,
  4416. "failed to send dirsync search request: %d\n", rc);
  4417. goto error;
  4418. }
  4419. while ( (e = windows_conn_get_search_result(prp->conn) ) != NULL)
  4420. {
  4421. rc = windows_process_dirsync_entry(prp,e,0);
  4422. if (e)
  4423. {
  4424. slapi_entry_free(e);
  4425. }
  4426. } /* While entry != NULL */
  4427. if (!windows_private_dirsync_has_more(prp->agmt))
  4428. {
  4429. done = 1;
  4430. }
  4431. } /* While !done */
  4432. error:
  4433. LDAPDebug( LDAP_DEBUG_TRACE, "<= windows_dirsync_inc_run\n", 0, 0, 0 );
  4434. }