windows_protocol_util.c 137 KB

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