ldaputil.c 41 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560
  1. /** --- BEGIN COPYRIGHT BLOCK ---
  2. * Copyright (C) 2001 Sun Microsystems, Inc. Used by permission.
  3. * Copyright (C) 2005 Red Hat, Inc.
  4. * All rights reserved.
  5. --- END COPYRIGHT BLOCK --- */
  6. /*
  7. * ldaputil.c -- LDAP utility functions -- HTTP gateway
  8. */
  9. #include "dsgw.h"
  10. #include "dbtdsgw.h"
  11. #include "../../include/disptmpl.h"
  12. #ifndef NO_LIBLCACHE
  13. #include <lcache.h>
  14. #endif
  15. #if XP_WIN32
  16. #include <windows.h>
  17. #include <io.h>
  18. #include <fcntl.h>
  19. #endif
  20. static dsgwtmplinfo *init_listdisplay( char *tmplname, unsigned long options );
  21. static int do_search( dsgwtmplinfo *tip, LDAP *ld, char *base, int scope,
  22. char *filter, LDAPMessage **msgpp );
  23. static void handle_search_results( dsgwtmplinfo *tip, LDAP *ld, int rc,
  24. LDAPMessage *msgp, unsigned long options );
  25. static int LDAP_CALL LDAP_CALLBACK
  26. get_rebind_credentials( LDAP *ld, char **whop, char **credp,
  27. int *methodp, int freeit, void *arg );
  28. static void strcpy_special_undo( char *d, char *s );
  29. static int entry2htmlwrite( void *fp, char *buf, int len );
  30. static void emit_one_loc_dn( char *dn, char *friendlyname, char *rootname,
  31. int only_one );
  32. static char *uid2dn( LDAP *ld, char *uid, char *base, int *ldaprc,
  33. char **lderrtxtp, char **errsp );
  34. static void return_one_attr( LDAP *ld, LDAPMessage *entry, char *attrtype,
  35. char *mimetype, int valindex );
  36. static void break_up_one_attr( char *attr, char **attrtypep, char **mimetypep,
  37. int *valindexp );
  38. /* binddn and bindpasswd are used in get_rebind_credentials() */
  39. static char *binddn = NULL, *bindpasswd = NULL;
  40. #ifndef DSGW_NO_SSL
  41. /*static CERTCertDBHandle certdbh;*/
  42. static char * certdbh;
  43. #endif
  44. /*
  45. * initialize various LDAP library things -- any non-NULL parameters are
  46. * initialized and set. If an error occurs, this function will not
  47. * return at all.
  48. * If an LDAP connection was opened, this function will return either
  49. * DSGW_BOUND_ASUSER if a valid cookie was found in the environment
  50. * and we were able to bind to the directory as that user. If no
  51. * cookie was found, or the cookie would not be used to bind, then
  52. * an anonymous bind is performed and DSGW_BOUND_ANONYMOUS is returned.
  53. * If skipac (skip authentication check) is non-zero, then this
  54. * function will always authenticate as NULL.
  55. *
  56. * If we are configured to use a local LDAP database instead of a real
  57. * directory server, we always do an unauthenticated bind but we return
  58. * DSGW_BOUND_ASUSER. This is done to keep our CGIs that check for a
  59. * return code of DSGW_BOUND_ASUSER happy.
  60. *
  61. * If skipauthwarning is set, then we don't display the javascript
  62. * auth warning for searches. - RJP
  63. */
  64. int
  65. dsgw_init_ldap( LDAP **ldp, LDAPFiltDesc **lfdpp, int skipac, int skipauthwarning )
  66. {
  67. char *path;
  68. char *userid, *dn, *rndstr, *passwd, *cookie, *p;
  69. int ret = 0, optval, limit;
  70. #ifdef XP_WIN32
  71. WSADATA wsadata;
  72. #endif
  73. /* LDAP search filters */
  74. if ( lfdpp != NULL ) {
  75. path = dsgw_file2path( gc->gc_configdir, DSGW_FILTERFILE );
  76. if (( *lfdpp = ldap_init_getfilter( path )) == NULL ) {
  77. dsgw_error( DSGW_ERR_BADCONFIG, path, DSGW_ERROPT_EXIT, 0, NULL );
  78. }
  79. free( path );
  80. ret = 0;
  81. }
  82. #ifdef XP_WIN32
  83. if( ret = WSAStartup(0x0101, &wsadata ) != 0 )
  84. dsgw_error( DSGW_ERR_WSAINIT, NULL, DSGW_ERROPT_EXIT, 0, NULL );
  85. #endif /* XP_WIN32 */
  86. /* LDAP connection */
  87. if ( ldp != NULL ) {
  88. if ( gc == NULL ) {
  89. dsgw_error( DSGW_ERR_INTERNAL,
  90. XP_GetClientStr(DBT_ldapInitLcacheInitAttemptedBefor_),
  91. DSGW_ERROPT_EXIT, 0, NULL );
  92. }
  93. if ( gc->gc_localdbconf == NULL ) {
  94. /* "Real LDAP server" case */
  95. #ifdef DSGW_NO_SSL
  96. *ldp = ldap_init( gc->gc_ldapserver, gc->gc_ldapport );
  97. #else /* DSGW_NO_SSL */
  98. if ( gc->gc_ldapssl ) {
  99. if ( gc->gc_securitypath == NULL ) {
  100. dsgw_error( DSGW_ERR_NOSECPATH, NULL, DSGW_ERROPT_EXIT,
  101. 0, NULL );
  102. }
  103. if ( ldapssl_client_init( gc->gc_securitypath,
  104. &certdbh ) < 0 ) {
  105. dsgw_error( DSGW_ERR_SSLINIT, gc->gc_securitypath,
  106. DSGW_ERROPT_EXIT, 0, NULL );
  107. }
  108. *ldp = ldapssl_init( gc->gc_ldapserver, gc->gc_ldapport, 1 );
  109. dsgw_NSSInitializedAlready = 1;
  110. } else {
  111. *ldp = ldap_init( gc->gc_ldapserver, gc->gc_ldapport );
  112. }
  113. #endif /* !DSGW_NO_SSL */
  114. if ( *ldp == NULL ) {
  115. dsgw_error( DSGW_ERR_LDAPINIT, NULL, DSGW_ERROPT_EXIT, 0,
  116. NULL );
  117. }
  118. }
  119. #ifndef NO_LIBLCACHE
  120. else {
  121. /* Local DB case */
  122. if (( *ldp = ldap_init( NULL, 0 )) == NULL ) {
  123. dsgw_error( DSGW_ERR_LDAPINIT, NULL, DSGW_ERROPT_EXIT, 0,
  124. NULL );
  125. }
  126. if ( lcache_init( *ldp, gc->gc_localdbconf ) != 0 ) {
  127. dsgw_error( DSGW_ERR_LCACHEINIT, strerror(errno),
  128. DSGW_ERROPT_EXIT, 0, NULL );
  129. }
  130. optval = 1;
  131. (void) ldap_set_option( *ldp, LDAP_OPT_CACHE_ENABLE, &optval );
  132. optval = LDAP_CACHE_LOCALDB;
  133. (void) ldap_set_option( *ldp, LDAP_OPT_CACHE_STRATEGY, &optval );
  134. }
  135. #endif
  136. rndstr = dn = NULL;
  137. passwd = dsgw_get_cgi_var( "passwd", DSGW_CGIVAR_OPTIONAL );
  138. if (( p = dsgw_get_cgi_var( "ldapsizelimit", DSGW_CGIVAR_OPTIONAL ))
  139. != NULL ) {
  140. limit = atoi( p );
  141. (void) ldap_set_option( *ldp, LDAP_OPT_SIZELIMIT, &limit );
  142. }
  143. if (( p = dsgw_get_cgi_var( "ldaptimelimit", DSGW_CGIVAR_OPTIONAL ))
  144. != NULL ) {
  145. limit = atoi( p );
  146. (void) ldap_set_option( *ldp, LDAP_OPT_TIMELIMIT, &limit );
  147. }
  148. /*
  149. * we don't bother with authentication if:
  150. * the "skipac" flag is non-zero OR
  151. * no "passwd" form element was passed in and we are using local db
  152. */
  153. if ( !skipac && ( passwd != NULL || gc->gc_localdbconf == NULL )) {
  154. /*
  155. * There are several ways in which authentication might
  156. * happen.
  157. */
  158. if ( gc->gc_admserv ) {
  159. /*
  160. * We're running under the admin server, so ask libadmin
  161. * for the user's credentials. If a password comes as a form
  162. * field, it overrides value we get from admin server
  163. */
  164. (void)dsgw_get_adm_identity( *ldp, &userid, &dn,
  165. ( passwd == NULL ) ? &passwd : NULL, DSGW_ERROPT_EXIT );
  166. #ifdef DSGW_DEBUG
  167. dsgw_log( "dsgw_init_ldap: run under admserv, user id = %s, "
  168. "dn = %s, passwd = %s, skipac = %d, dn = 0x%x\n",
  169. userid == NULL ? "NULL" : userid,
  170. dn == NULL ? "NULL" : dn,
  171. passwd == NULL ? "NULL" : passwd,
  172. skipac, dn );
  173. #endif
  174. } else {
  175. /*
  176. * Not running under admin server. The DN and password
  177. * might come in as form fields, or the authentication
  178. * might be accomplished via a client-side cookie which
  179. * gets looked up in the gateway's cookie database.
  180. */
  181. /* check for dn/binddn in request */
  182. if ( passwd != NULL ) {
  183. if (( dn = dsgw_get_escaped_cgi_var( "escapedbinddn",
  184. "binddn", DSGW_CGIVAR_OPTIONAL )) == NULL &&
  185. ( dn = dsgw_get_cgi_var( "dn",
  186. DSGW_CGIVAR_OPTIONAL )) == NULL ) {
  187. free( passwd );
  188. passwd = NULL;
  189. } else {
  190. /* got DN: undo extra level of escaping */
  191. dsgw_form_unescape( dn );
  192. }
  193. }
  194. if ( passwd == NULL ) {
  195. /* Check for a valid authentication cookie */
  196. cookie = dsgw_get_auth_cookie();
  197. if ( cookie != NULL ) {
  198. if ( dsgw_parse_cookie( cookie, &rndstr, &dn ) == 0 ) {
  199. int ckrc;
  200. if (( ckrc = dsgw_ckdn2passwd( rndstr, dn,
  201. &passwd )) != 0 ) {
  202. passwd = NULL;
  203. dn = NULL;
  204. /*
  205. * Delete the cookie and print out the error message.
  206. * dn2passwd_error() returns 1 if the CGI should exit,
  207. * 0 if it should continue.
  208. */
  209. if (dsgw_dn2passwd_error( ckrc, skipauthwarning )) {
  210. exit( 0 );
  211. }
  212. }
  213. }
  214. }
  215. if ( rndstr != NULL ) {
  216. free( rndstr );
  217. }
  218. if ( cookie != NULL ) {
  219. free( cookie );
  220. }
  221. }
  222. }
  223. }
  224. /*
  225. * try to use LDAP version 3 but fall back to v2 if bind fails
  226. */
  227. optval = LDAP_VERSION3;
  228. (void)ldap_set_option( *ldp, LDAP_OPT_PROTOCOL_VERSION, &optval );
  229. /*
  230. * If everything above failed to set the dn/password, then use
  231. * the binddn and bindpw, if any.
  232. */
  233. if (dn == NULL && passwd == NULL &&
  234. strlen(gc->gc_binddn) > 0 && strlen(gc->gc_bindpw) > 0) {
  235. dn = dsgw_ch_strdup(gc->gc_binddn);
  236. passwd = dsgw_ch_strdup(gc->gc_bindpw);
  237. }
  238. if (( ret = ldap_simple_bind_s( *ldp, dn, passwd ))
  239. == LDAP_PROTOCOL_ERROR ) {
  240. optval = LDAP_VERSION2;
  241. (void)ldap_set_option( *ldp, LDAP_OPT_PROTOCOL_VERSION,
  242. &optval );
  243. ret = ldap_simple_bind_s( *ldp, dn, passwd );
  244. }
  245. if ( ret != LDAP_SUCCESS ){
  246. dsgw_ldap_error( *ldp, DSGW_ERROPT_DURINGBIND );
  247. /* Display back button */
  248. dsgw_form_begin( NULL, NULL );
  249. dsgw_emits( "\n<CENTER><TABLE border=2 width=\"100%\"><TR>\n" );
  250. dsgw_emits( "<TD WIDTH=\"100%\" ALIGN=\"center\">\n" );
  251. dsgw_emitf( "<INPUT TYPE=\"button\" VALUE=\"%s\" "
  252. "onClick=\"history.back()\">\n",
  253. XP_GetClientStr(DBT_goBack_) );
  254. dsgw_emits( "\n</TABLE></CENTER></FORM>\n" );
  255. exit(0);
  256. }
  257. if (( dn != NULL ) && ( passwd != NULL )) {
  258. ret = DSGW_BOUND_ASUSER;
  259. binddn = dn;
  260. bindpasswd = passwd;
  261. ldap_set_rebind_proc( *ldp, get_rebind_credentials, NULL );
  262. } else if ( gc->gc_localdbconf != NULL ) {
  263. ret = DSGW_BOUND_ASUSER; /* a small, harmless lie */
  264. } else {
  265. ret = DSGW_BOUND_ANONYMOUS;
  266. }
  267. }
  268. return ret;
  269. }
  270. /*
  271. * get user identity from the admin. server (if running under it)
  272. * if uidp is non-NULL, it is set to point to user's login id.
  273. * if dnp is non-NULL, it is set to point to user's DN.
  274. * if pwdp is non-NULL, it is set to point to user's password.
  275. * Returns: 0 if all goes well, -1 if an error occurs.
  276. *
  277. * Note that ld is used only if dnp != NULL, and then only if the admin server
  278. * returns NULL when asked for the DN.
  279. */
  280. int
  281. dsgw_get_adm_identity( LDAP *ld, char **uidp, char **dnp, char **pwdp,
  282. int erropts )
  283. {
  284. int rc, need_to_get_dn;
  285. char *uid;
  286. static int adm_inited = 0;
  287. if ( !gc->gc_admserv ) {
  288. dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL,
  289. XP_GetClientStr(DBT_notRunningUnderTheAdministration_),
  290. erropts, 0, NULL );
  291. return( -1 );
  292. }
  293. if ( !adm_inited ) {
  294. if ( ADM_InitializePermissions( &rc ) < 0 ) {
  295. dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL,
  296. XP_GetClientStr(DBT_couldNotInitializePermissions_),
  297. erropts, 0, NULL );
  298. return( -1 );
  299. }
  300. adm_inited = 1;
  301. }
  302. need_to_get_dn = ( dnp != NULL );
  303. if ( need_to_get_dn && ADM_GetUserDNString( &rc, dnp ) < 0 ) {
  304. dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL,
  305. XP_GetClientStr(DBT_couldNotMapUsernameToADnErrorFro_),
  306. erropts, 0, NULL );
  307. return( -1 );
  308. }
  309. /*
  310. * get userid if:
  311. * 1. requested by caller (uidp != NULL)
  312. * or 2. DN was requested but Admin Server didn't return the DN
  313. */
  314. if (( uidp != NULL || ( need_to_get_dn && *dnp == NULL )) &&
  315. ( ADM_GetCurrentUsername( &rc, &uid ) < 0 || uid == NULL )) {
  316. dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL,
  317. XP_GetClientStr(DBT_couldNotGetCurrentUsername_), erropts,
  318. 0, NULL );
  319. return( -1 );
  320. }
  321. if ( uidp != NULL ) {
  322. *uidp = uid;
  323. }
  324. if ( need_to_get_dn && *dnp == NULL ) {
  325. /*
  326. * try to map userid to DN using LDAP search
  327. */
  328. int lderr;
  329. char *errstr, *lderrtxt;
  330. if (( *dnp = uid2dn( ld, uid, gc->gc_ldapsearchbase, &lderr,
  331. &lderrtxt, &errstr )) == NULL ) {
  332. dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL, errstr, erropts, lderr,
  333. lderrtxt );
  334. return( -1 );
  335. }
  336. }
  337. if ( pwdp != NULL && ADM_GetCurrentPassword( &rc, pwdp ) < 0 ) {
  338. dsgw_error( DSGW_ERR_ADMSERV_CREDFAIL,
  339. XP_GetClientStr(DBT_couldNotGetCurrentUserPassword_), erropts,
  340. 0, NULL );
  341. return( -1 );
  342. }
  343. return( 0 );
  344. }
  345. void
  346. dsgw_ldap_error( LDAP *ld, int erropts )
  347. {
  348. int lderr;
  349. char *lderrtxt = NULL;
  350. lderr = ldap_get_lderrno( ld, NULL, &lderrtxt );
  351. dsgw_error( DSGW_ERR_LDAPGENERAL, dsgw_ldaperr2string( lderr ),
  352. erropts, lderr, lderrtxt );
  353. }
  354. struct ldap_searchobj *
  355. dsgw_type2searchobj( struct ldap_searchobj *solistp, char *type )
  356. {
  357. struct ldap_searchobj *sop;
  358. for ( sop = ldap_first_searchobj( solistp ); sop != NULL;
  359. sop = ldap_next_searchobj( solistp, sop )) {
  360. if ( strcasecmp( type, sop->so_objtypeprompt ) == 0 ) {
  361. return( sop );
  362. }
  363. }
  364. return( NULL );
  365. }
  366. struct ldap_searchattr *
  367. dsgw_label2searchattr( struct ldap_searchobj *sop, char *label )
  368. {
  369. struct ldap_searchattr *sap;
  370. for ( sap = sop->so_salist; sap != NULL; sap = sap->sa_next ) {
  371. if ( strcasecmp( label, sap->sa_attrlabel ) == 0 ) {
  372. return( sap );
  373. }
  374. }
  375. return( NULL );
  376. }
  377. struct ldap_searchmatch *
  378. dsgw_prompt2searchmatch( struct ldap_searchobj *sop, char *prompt )
  379. {
  380. struct ldap_searchmatch *smp;
  381. for ( smp = sop->so_smlist; smp != NULL; smp = smp->sm_next ) {
  382. if ( strcasecmp( prompt, smp->sm_matchprompt ) == 0 ) {
  383. return( smp );
  384. }
  385. }
  386. return( NULL );
  387. }
  388. static dsgwtmplinfo *
  389. init_listdisplay( char *tmplname, unsigned long options )
  390. {
  391. char *s;
  392. if (( s = dsgw_get_cgi_var( "listtemplate", DSGW_CGIVAR_OPTIONAL ))
  393. != NULL ) {
  394. tmplname = s;
  395. }
  396. return( dsgw_display_init( DSGW_TMPLTYPE_LIST, tmplname, options ));
  397. }
  398. void
  399. dsgw_smart_search( LDAP *ld, struct ldap_searchobj *sop, LDAPFiltDesc *lfdp,
  400. char *base, char *value, unsigned long options )
  401. {
  402. int rc;
  403. LDAPFiltInfo *lfip;
  404. dsgwtmplinfo *tip;
  405. LDAPMessage *msgp;
  406. ldap_setfilteraffixes( lfdp, sop->so_filterprefix, NULL );
  407. tip = init_listdisplay( sop->so_objtypeprompt, options );
  408. if (( lfip = ldap_getfirstfilter( lfdp, sop->so_filtertag, value ))
  409. == NULL ) {
  410. dsgw_error( DSGW_ERR_NOFILTERS, sop->so_objtypeprompt,
  411. DSGW_ERROPT_EXIT, 0, NULL );
  412. }
  413. for ( ; lfip != NULL; lfip = ldap_getnextfilter( lfdp )) {
  414. dsgw_set_searchdesc( tip, NULL, lfip->lfi_desc, value );
  415. rc = do_search( tip, ld, base, sop->so_defaultscope, lfip->lfi_filter,
  416. &msgp );
  417. if ( rc != LDAP_SUCCESS ||
  418. ( msgp != NULL && ldap_count_entries( ld, msgp ) > 0 )) {
  419. if ( strstr( lfip->lfi_filter, "~=" ) != NULL ) {
  420. /* always list if approximate filter used to find entry */
  421. options |= DSGW_DISPLAY_OPT_LIST_IF_ONE;
  422. }
  423. break; /* error or got some entries: stop searching */
  424. }
  425. }
  426. handle_search_results( tip, ld, rc, msgp, options );
  427. }
  428. void
  429. dsgw_pattern_search( LDAP *ld, char *listtmpl,
  430. char *searchdesc2, char *searchdesc3, char *searchdesc4,
  431. char *filtpattern, char *filtprefix, char *filtsuffix, char *attr,
  432. char *base, int scope, char *value, unsigned long options )
  433. {
  434. char buf[ 4096 ];
  435. int rc;
  436. dsgwtmplinfo *tip;
  437. LDAPMessage *msgp;
  438. tip = init_listdisplay( listtmpl, options );
  439. ldap_build_filter( buf, sizeof( buf ), filtpattern,
  440. filtprefix, filtsuffix, attr, value, NULL );
  441. dsgw_set_searchdesc( tip, searchdesc2, searchdesc3, searchdesc4 );
  442. rc = do_search( tip, ld, base, scope, buf, &msgp );
  443. handle_search_results( tip, ld, rc, msgp, options );
  444. }
  445. /*
  446. * Perform URL-based search.
  447. * Note that if "ld" is NULL, this routine sets gc->gc_ldapserver and
  448. * gc->gc_ldapport globals itself, calls dsgw_init_ldap(), and then does
  449. * the URL-based search. If "ld" is not NULL, no initialization is done
  450. * here.
  451. */
  452. void
  453. dsgw_ldapurl_search( LDAP *ld, char *ldapurl )
  454. {
  455. int rc, ec, saveport, did_init_ldap;
  456. LDAPMessage *msgp;
  457. LDAPURLDesc *ludp;
  458. char *saveserver;
  459. unsigned long no_options = 0;
  460. int one_attr = 0;
  461. if (( rc = ldap_url_parse( ldapurl, &ludp )) != 0 ) {
  462. switch ( rc ) {
  463. case LDAP_URL_ERR_NODN:
  464. ec = DSGW_ERR_LDAPURL_NODN;
  465. break;
  466. case LDAP_URL_ERR_BADSCOPE:
  467. ec = DSGW_ERR_LDAPURL_BADSCOPE;
  468. break;
  469. case LDAP_URL_ERR_MEM:
  470. ec = DSGW_ERR_NOMEMORY;
  471. break;
  472. case LDAP_URL_ERR_NOTLDAP:
  473. default:
  474. ec = DSGW_ERR_LDAPURL_NOTLDAP;
  475. break;
  476. }
  477. dsgw_error( ec, ldapurl, DSGW_ERROPT_EXIT, 0, NULL );
  478. }
  479. if ( ld == NULL ) {
  480. saveserver = gc->gc_ldapserver;
  481. gc->gc_ldapserver = ludp->lud_host;
  482. saveport = gc->gc_ldapport;
  483. gc->gc_ldapport = ludp->lud_port;
  484. one_attr = ( ludp->lud_attrs != NULL && ludp->lud_attrs[ 0 ] != NULL && ludp->lud_attrs[ 1 ] == NULL );
  485. (void)dsgw_init_ldap( &ld, NULL, 0, one_attr );
  486. did_init_ldap = 1;
  487. } else {
  488. did_init_ldap = 0;
  489. }
  490. /* XXX a bit of a hack: if it looks like only a DN was included, we
  491. * assume that a read of the entry is desired.
  492. */
  493. if ( ludp->lud_scope == LDAP_SCOPE_BASE && strcasecmp( ludp->lud_filter,
  494. "(objectClass=*)" ) == 0 ) {
  495. dsgw_read_entry( ld, ludp->lud_dn, NULL, NULL, ludp->lud_attrs,
  496. no_options );
  497. } else {
  498. dsgwtmplinfo *tip;
  499. dsgw_send_header();
  500. tip = init_listdisplay( "urlsearch", no_options );
  501. dsgw_set_searchdesc( tip, NULL, XP_GetClientStr(DBT_theLDAPFilterIs_), ldapurl );
  502. rc = do_search( tip, ld, ludp->lud_dn, ludp->lud_scope,
  503. ludp->lud_filter, &msgp );
  504. handle_search_results( tip, ld, rc, msgp, no_options );
  505. }
  506. if ( did_init_ldap ) {
  507. ldap_unbind( ld );
  508. gc->gc_ldapserver = saveserver;
  509. gc->gc_ldapport = saveport;
  510. }
  511. }
  512. /*
  513. * do the actual search over LDAP. Return an LDAP error code.
  514. */
  515. static int
  516. do_search( dsgwtmplinfo *tip, LDAP *ld, char *base, int scope, char *filter,
  517. LDAPMessage **msgpp )
  518. {
  519. char **attrlist, *attrs[ 3 ];
  520. *msgpp = NULL;
  521. if ( tip == NULL || tip->dsti_attrs == NULL ) {
  522. attrs[ 0 ] = DSGW_ATTRTYPE_OBJECTCLASS;
  523. if ( tip != NULL && tip->dsti_sortbyattr != NULL ) {
  524. attrs[ 1 ] = tip->dsti_sortbyattr;
  525. attrs[ 2 ] = NULL;
  526. } else {
  527. attrs[ 1 ] = NULL;
  528. }
  529. attrlist = attrs;
  530. } else {
  531. attrlist = tip->dsti_attrs;
  532. }
  533. #ifdef DSGW_DEBUG
  534. dsgw_log ("ldap_search_s(ld,\"%s\",%i,\"%s\")\n", base, scope, filter);
  535. #endif
  536. return( ldap_search_s( ld, base, scope, filter, attrlist, 0, msgpp ));
  537. }
  538. static int
  539. is_subtype( const char *sub, const char *sup )
  540. {
  541. auto const size_t subLen = strlen( sub );
  542. auto const size_t supLen = strlen( sup );
  543. if ( subLen < supLen ) return 0;
  544. if ( subLen == supLen ) return !strcasecmp( sub, sup );
  545. if ( sub[supLen] != ';' ) return 0;
  546. return !strncasecmp( sub, sup, strlen( sup ));
  547. }
  548. static const struct berval* LDAP_C LDAP_CALLBACK
  549. dsgw_keygen( void *arg, LDAP *ld, LDAPMessage *entry )
  550. {
  551. auto const char* sortbyattr = (char*)arg;
  552. auto struct berval* result = NULL;
  553. if (sortbyattr == NULL) { /* sort by DN */
  554. auto char* DN = ldap_get_dn( ld, entry );
  555. if (DN) {
  556. result = dsgw_strkeygen( CASE_INSENSITIVE, DN );
  557. ldap_memfree( DN );
  558. }
  559. } else {
  560. auto char* attr;
  561. auto BerElement *ber;
  562. for (attr = ldap_first_attribute( ld, entry, &ber ); attr != NULL;
  563. attr = ldap_next_attribute ( ld, entry, ber ) ) {
  564. auto char **vals;
  565. if ( is_subtype( attr, sortbyattr ) &&
  566. NULL != ( vals = ldap_get_values( ld, entry, attr ))) {
  567. auto size_t i;
  568. for ( i = 0; vals[i] != NULL; ++i ) {
  569. auto struct berval* key = dsgw_strkeygen( CASE_INSENSITIVE, vals[i] );
  570. if ( result == NULL || dsgw_keycmp( NULL, key, result ) < 0 ) {
  571. auto struct berval* tmp = result;
  572. result = key;
  573. key = tmp;
  574. #ifdef DSGW_DEBUG
  575. {
  576. auto char* ev = dsgw_strdup_escaped( vals[i] );
  577. auto char* DN = ldap_get_dn( ld, entry );
  578. dsgw_log( "dsgw_keygen(%s,%s) %p %s\n", sortbyattr, DN, (void*)result, ev );
  579. ldap_memfree( DN );
  580. free( ev );
  581. }
  582. #endif
  583. }
  584. if ( key != NULL ) {
  585. dsgw_keyfree( arg, key );
  586. }
  587. }
  588. ldap_value_free( vals );
  589. }
  590. ldap_memfree( attr );
  591. }
  592. if ( ber != NULL ) {
  593. ldap_ber_free( ber, 0 );
  594. }
  595. }
  596. return result ? result : /* no such attribute */ dsgw_key_last;
  597. }
  598. static void
  599. handle_search_results( dsgwtmplinfo *tip, LDAP *ld, int rc, LDAPMessage *msgp,
  600. unsigned long options )
  601. {
  602. int count;
  603. LDAPMessage *entry;
  604. char *dn, *errortext, *lderrtxt, **ocvals;
  605. count = ( msgp == NULL ) ? 0 : ldap_count_entries( ld, msgp );
  606. if ( rc == LDAP_SUCCESS ) {
  607. errortext = NULL;
  608. lderrtxt = NULL;
  609. } else {
  610. errortext = dsgw_ldaperr2string( rc );
  611. (void)ldap_get_lderrno( ld, NULL, &lderrtxt );
  612. }
  613. dsgw_set_search_result( tip, count, errortext, lderrtxt );
  614. if ( count > 0 ) {
  615. entry = ldap_first_entry( ld, msgp );
  616. if ( count == 1 && ( options & DSGW_DISPLAY_OPT_LIST_IF_ONE ) == 0 ) {
  617. /* found exactly one entry: read and display it */
  618. dn = ldap_get_dn( ld, entry );
  619. ocvals = ldap_get_values( ld, entry, DSGW_ATTRTYPE_OBJECTCLASS );
  620. ldap_msgfree( msgp );
  621. dsgw_read_entry( ld, dn, ocvals, NULL, NULL, options );
  622. if ( ocvals != NULL ) {
  623. ldap_value_free( ocvals );
  624. }
  625. return;
  626. }
  627. /* list entries */
  628. #ifdef DSGW_DEBUG
  629. dsgw_log( "handle_search_results: sort entries by %s\n",
  630. tip->dsti_sortbyattr ? tip->dsti_sortbyattr : "DN" );
  631. #endif
  632. ldap_keysort_entries( ld, &msgp, tip->dsti_sortbyattr,
  633. dsgw_keygen, dsgw_keycmp, dsgw_keyfree );
  634. for ( entry = ldap_first_entry( ld, msgp ); entry != NULL;
  635. entry = ldap_next_entry( ld, entry )) {
  636. dsgw_display_entry( tip, ld, entry, NULL, NULL );
  637. }
  638. if ( options & DSGW_DISPLAY_OPT_DNLIST_JS ) {
  639. int i;
  640. char *edn, *js0, *js1;
  641. char **xdn;
  642. char **sn;
  643. dsgw_emits( "<SCRIPT LANGUAGE=\"JavaScript\">\n" );
  644. dsgw_emits( "var dnlist = new Array;\n" );
  645. for ( i = 0, entry = ldap_first_entry( ld, msgp ); entry != NULL;
  646. i++, entry = ldap_next_entry( ld, entry )) {
  647. dn = ldap_get_dn( ld, entry );
  648. edn = dsgw_strdup_escaped( dn );
  649. xdn = ldap_explode_dn( dn, 1 );
  650. dsgw_emitf( "dnlist[%d] = new Object\n", i );
  651. dsgw_emitf( "dnlist[%d].edn = '%s';\n", i, edn );
  652. js0 = dsgw_escape_quotes( xdn[ 0 ] );
  653. if ( xdn[1] != NULL ) {
  654. js1 = dsgw_escape_quotes( xdn[ 1 ] );
  655. dsgw_emitf( "dnlist[%d].rdn = '%s, %s';\n", i, js0, js1 );
  656. free( js1 );
  657. } else {
  658. dsgw_emitf( "dnlist[%d].rdn = '%s';\n", i, js0 );
  659. }
  660. free( js0 );
  661. if (( sn = ldap_get_values( ld, entry, "sn" )) == NULL ) {
  662. js0 = NULL;
  663. } else {
  664. js0 = dsgw_escape_quotes( sn[ 0 ] );
  665. ldap_value_free( sn );
  666. }
  667. dsgw_emitf( "dnlist[%d].sn = '%s';\n", i, ( js0 == NULL ) ?
  668. " " : js0 );
  669. if ( js0 != NULL ) {
  670. free( js0 );
  671. }
  672. dsgw_emitf( "dnlist[%d].selected = false;\n", i );
  673. free( edn );
  674. ldap_value_free( xdn );
  675. ldap_memfree( dn );
  676. }
  677. dsgw_emitf( "dnlist.count = %d;\n", i );
  678. dsgw_emitf( "</SCRIPT>\n" );
  679. }
  680. ldap_msgfree( msgp );
  681. } else {
  682. /* Count <= 0 */
  683. if ( options & DSGW_DISPLAY_OPT_DNLIST_JS ) {
  684. dsgw_emitf( "<SCRIPT LANGUAGE=\"JavaScript\">\n" );
  685. dsgw_emitf( "var dnlist = new Array;\n" );
  686. dsgw_emitf( "dnlist.count = 0;\n" );
  687. dsgw_emitf( "</SCRIPT>\n" );
  688. }
  689. }
  690. dsgw_display_done( tip );
  691. }
  692. /*
  693. * read and display a single entry. If ocvals is non-NULL, it should
  694. * contain the list of objectClass values for this entry.
  695. */
  696. void
  697. dsgw_read_entry( LDAP *ld, char *dn, char **ocvals, char *tmplname,
  698. char **attrs, unsigned long options )
  699. {
  700. int rc, one_attr, freeocvals, valindex;
  701. char *tmpattr, *attr0, *mimetype;
  702. LDAPMessage *msgp, *entry, *aomsgp, *aoentry;
  703. dsgwtmpl *tmpl;
  704. dsgwtmplinfo *tip;
  705. if (( options & DSGW_DISPLAY_OPT_AUTH ) != 0 ) {
  706. /*
  707. * XXX hack -- if we are trying to authenticate, we don't generate an
  708. * entry display at all. Instead, we generate an authenticate form.
  709. */
  710. dsgw_send_header();
  711. dsgw_emit_auth_form( dn );
  712. return;
  713. }
  714. one_attr = ( attrs != NULL && attrs[ 0 ] != NULL && attrs[ 1 ] == NULL );
  715. if ( one_attr ) {
  716. break_up_one_attr( attrs[ 0 ], &tmpattr, &mimetype, &valindex );
  717. if ( strcasecmp( tmpattr, "_vcard" ) == 0 ) { /* VCards are special */
  718. dsgw_vcard_from_entry( ld, dn, mimetype );
  719. return;
  720. }
  721. attr0 = attrs[ 0 ]; /* replace first & only attr. */
  722. attrs[ 0 ] = tmpattr;
  723. } else {
  724. attr0 = NULL;
  725. }
  726. if ( tmplname == NULL && ( tmplname = dsgw_get_cgi_var( "displaytemplate",
  727. DSGW_CGIVAR_OPTIONAL )) == NULL && attrs == NULL ) {
  728. /* determine what display template to use based on objectClass values */
  729. freeocvals = 0;
  730. if ( ocvals == NULL ) { /* read entry to get objectClasses */
  731. char *attrs[ 2 ];
  732. attrs[ 0 ] = DSGW_ATTRTYPE_OBJECTCLASS;
  733. attrs[ 1 ] = NULL;
  734. if (( rc = ldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
  735. attrs, 0, &msgp )) != LDAP_SUCCESS ||
  736. ( entry = ldap_first_entry( ld, msgp )) == NULL ) {
  737. dsgw_ldap_error( ld, DSGW_ERROPT_EXIT );
  738. }
  739. ocvals = ldap_get_values( ld, msgp, DSGW_ATTRTYPE_OBJECTCLASS );
  740. freeocvals = 1;
  741. ldap_msgfree( msgp );
  742. }
  743. if ( ocvals == NULL || ( tmpl = dsgw_oc2template( ocvals )) == NULL ) {
  744. tmplname = NULL;
  745. } else {
  746. tmplname = tmpl->dstmpl_name;
  747. }
  748. if ( freeocvals ) {
  749. ldap_value_free( ocvals );
  750. }
  751. }
  752. if ( tmplname == NULL ) {
  753. tip = NULL;
  754. if ( !one_attr ) {
  755. char *title;
  756. if (( title = ldap_dn2ufn( dn )) == NULL ) {
  757. title = dn;
  758. }
  759. dsgw_send_header();
  760. dsgw_html_begin( title, 1 );
  761. dsgw_emitf( "<FONT SIZE=\"+1\">\n%s\n</FONT>\n",
  762. XP_GetClientStr(DBT_noteThereIsNoDisplayTemplateForT_) );
  763. }
  764. } else if (( tip = dsgw_display_init( DSGW_TMPLTYPE_DISPLAY, tmplname,
  765. options )) != NULL ) {
  766. dsgw_send_header();
  767. attrs = tip->dsti_attrs;
  768. }
  769. /* now read the attributes needed for the template */
  770. if (( rc = ldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
  771. attrs, 0, &msgp )) != LDAP_SUCCESS ) {
  772. dsgw_ldap_error( ld, DSGW_ERROPT_EXIT );
  773. }
  774. if (( entry = ldap_first_entry( ld, msgp )) == NULL ) {
  775. ldap_msgfree( msgp );
  776. dsgw_ldap_error( ld, DSGW_ERROPT_EXIT );
  777. }
  778. /* and retrieve attribute types only if we need any of them */
  779. if ( one_attr || tip == NULL || tip->dsti_attrsonly_attrs == NULL ) {
  780. aomsgp = NULL;
  781. } else {
  782. if (( rc = ldap_search_s( ld, dn, LDAP_SCOPE_BASE, "objectClass=*",
  783. tip->dsti_attrsonly_attrs, 1, &aomsgp )) != LDAP_SUCCESS ) {
  784. dsgw_ldap_error( ld, DSGW_ERROPT_EXIT );
  785. }
  786. /*
  787. * if no entries were returned, "aoentry" will be set to NULL by the
  788. * next statement. We don't treat that as an error since we know the
  789. * entry exists. It probably just means none of the "attrsonly" types
  790. * were present in the entry.
  791. */
  792. aoentry = ldap_first_entry( ld, aomsgp );
  793. }
  794. /* display it (finally!) */
  795. if ( one_attr ) {
  796. return_one_attr( ld, entry, attrs[ 0 ], mimetype, valindex );
  797. } else if ( tip == NULL ) {
  798. /* no template available -- display in an ugly but complete manner */
  799. if (( rc = ldap_entry2html( ld, NULL, entry, NULL, NULL, NULL,
  800. entry2htmlwrite, stdout, "\n", 0, LDAP_DISP_OPT_HTMLBODYONLY,
  801. NULL, NULL )) != LDAP_SUCCESS ) {
  802. dsgw_ldap_error( ld, DSGW_ERROPT_EXIT );
  803. }
  804. dsgw_html_end();
  805. } else {
  806. /* use template to create a nicely formatted display */
  807. dsgw_display_entry( tip, ld, entry, aoentry, NULL );
  808. dsgw_display_done( tip );
  809. }
  810. if ( attr0 != NULL ) {
  811. attrs[ 0 ] = attr0; /* if we replaced this, put original back */
  812. }
  813. if ( msgp != NULL ) {
  814. ldap_msgfree( msgp );
  815. }
  816. if ( aomsgp != NULL ) {
  817. ldap_msgfree( aomsgp );
  818. }
  819. }
  820. /*
  821. * return 1 if the entry already exists, 0 if not, -1 if some error occurs
  822. */
  823. int
  824. dsgw_ldap_entry_exists( LDAP *ld, char *dn, char **matchedp,
  825. unsigned long erropts )
  826. {
  827. LDAPMessage *msgp;
  828. int rc;
  829. msgp = NULL;
  830. if ( matchedp != NULL ) {
  831. *matchedp = NULL;
  832. }
  833. if (( rc = do_search( NULL, ld, dn, LDAP_SCOPE_BASE, "(objectClass=*)",
  834. &msgp )) != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT ) {
  835. dsgw_ldap_error( ld, erropts );
  836. }
  837. if ( msgp == NULL || rc == LDAP_NO_SUCH_OBJECT ) {
  838. rc = 0;
  839. if ( matchedp != NULL ) {
  840. (void)ldap_get_lderrno( ld, matchedp, NULL );
  841. }
  842. } else {
  843. rc = ( ldap_count_entries( ld, msgp ) > 0 ? 1 : 0 );
  844. ldap_msgfree( msgp );
  845. }
  846. return( rc );
  847. }
  848. static int
  849. entry2htmlwrite( void *fp, char *buf, int len )
  850. {
  851. return( fwrite( buf, len, 1, (FILE *)fp ) == 0 ? -1 : len );
  852. }
  853. /*
  854. * return 1 if the entry's parent exists, 0 if not, -1 if some error occurs.
  855. * If the entry is the same as gc->gc_ldapsearchbase, then we return 1,
  856. * so we don't prevent people from adding their organizational entry.
  857. */
  858. int
  859. dsgw_ldap_parent_exists( LDAP *ld, char *dn, unsigned long erropts )
  860. {
  861. LDAPMessage *msgp;
  862. int rc;
  863. /* Is "dn" == gc->gc_ldapsearchbase? */
  864. msgp = NULL;
  865. if (( rc = do_search( NULL, ld, dn, LDAP_SCOPE_BASE, "(objectClass=*)",
  866. &msgp )) != LDAP_SUCCESS && rc != LDAP_NO_SUCH_OBJECT ) {
  867. dsgw_ldap_error( ld, erropts );
  868. }
  869. if ( msgp == NULL ) {
  870. rc = 0;
  871. } else {
  872. rc = ( ldap_count_entries( ld, msgp ) > 0 ? 1 : 0 );
  873. ldap_msgfree( msgp );
  874. }
  875. return( rc );
  876. }
  877. /*
  878. * this function is called back by LIBLDAP when chasing referrals
  879. */
  880. static int LDAP_CALL LDAP_CALLBACK
  881. get_rebind_credentials( LDAP *ld, char **whop, char **credp,
  882. int *methodp, int freeit, void *arg )
  883. {
  884. if ( !freeit ) {
  885. *whop = binddn;
  886. *credp = bindpasswd;
  887. *methodp = LDAP_AUTH_SIMPLE;
  888. }
  889. return( LDAP_SUCCESS );
  890. }
  891. char *
  892. dsgw_get_binddn()
  893. {
  894. return( binddn );
  895. }
  896. /*
  897. * return 1 if bound using "dn"
  898. * return 0 if definitely bound as someone else
  899. * return "def_answer" is we can't tell for sure
  900. */
  901. int
  902. dsgw_bound_as_dn( char *dn, int def_answer )
  903. {
  904. int i, rc;
  905. char **rdns1, **rdns2;
  906. if ( binddn == NULL ) {
  907. /*
  908. * not authenticated: if not using local db or using it as an
  909. * end-user, return the default
  910. */
  911. if ( gc->gc_localdbconf == NULL || gc->gc_enduser ) {
  912. return( def_answer );
  913. }
  914. /*
  915. * if using local db as an admin, return "bound as someone else"
  916. * since there is no access control enforced anyways.
  917. */
  918. return( 0 );
  919. }
  920. /* first try a simple case-insensitive comparison */
  921. if ( strcasecmp( binddn, dn ) == 0 ) {
  922. return( 1 ); /* DNs are the same */
  923. }
  924. /*
  925. * These DNs may not have the same spacing or punctuation. Compare RDN
  926. * components to eliminate any differences.
  927. */
  928. if (( rdns1 = ldap_explode_dn( binddn, 0 )) == NULL ) {
  929. return( def_answer ); /* we don't know: return the default */
  930. }
  931. if (( rdns2 = ldap_explode_dn( dn, 0 )) == NULL ) {
  932. ldap_value_free( rdns1 );
  933. return( def_answer ); /* we don't know: return the default */
  934. }
  935. for ( i = 0; rdns1[ i ] != NULL && rdns2[ i ] != NULL; ++i ) {
  936. if ( strcasecmp( rdns1[ i ], rdns2[ i ] ) != 0 ) {
  937. break; /* DNs are not the same */
  938. }
  939. }
  940. rc = ( rdns1[ i ] == NULL && rdns2[ i ] == NULL );
  941. ldap_value_free( rdns1 );
  942. ldap_value_free( rdns2 );
  943. return( rc );
  944. }
  945. /*
  946. * Compare 2 DNs. Return 1 if they are equivalent, 0 if not.
  947. */
  948. int
  949. dsgw_dn_cmp( char *dn1, char *dn2 )
  950. {
  951. int i, rc;
  952. char **rdns1, **rdns2;
  953. /* first try a simple case-insensitive comparison */
  954. if ( dsgw_utf8casecmp( (unsigned char *)dn1, (unsigned char *)dn2 ) == 0 ) {
  955. return( 1 ); /* DNs are the same */
  956. }
  957. /*
  958. * These DNs may not have the same spacing or punctuation. Compare RDN
  959. * components to eliminate any differences.
  960. */
  961. if (( rdns1 = ldap_explode_dn( dn1, 0 )) == NULL ) {
  962. return( 0 ); /* we don't know: return 0 */
  963. }
  964. if (( rdns2 = ldap_explode_dn( dn2, 0 )) == NULL ) {
  965. ldap_value_free( rdns1 );
  966. return( 0 ); /* we don't know: return 0 */
  967. }
  968. for ( i = 0; rdns1[ i ] != NULL && rdns2[ i ] != NULL; ++i ) {
  969. if ( dsgw_utf8casecmp( (unsigned char *)rdns1[ i ], (unsigned char *)rdns2[ i ] ) != 0 ) {
  970. break; /* DNs are not the same */
  971. }
  972. }
  973. rc = ( rdns1[ i ] == NULL && rdns2[ i ] == NULL );
  974. ldap_value_free( rdns1 );
  975. ldap_value_free( rdns2 );
  976. return( rc );
  977. }
  978. /*
  979. * Return the parent of dn. The caller is responsible for freeing the
  980. * returned value. Returns NULL on error.
  981. */
  982. char *
  983. dsgw_dn_parent( char *dn )
  984. {
  985. char *dnp;
  986. int i;
  987. char **rdns;
  988. if ( dn == NULL ) {
  989. return( NULL );
  990. }
  991. dnp = dsgw_ch_malloc( strlen( dn ));
  992. dnp[ 0 ] = '\0';
  993. if (( rdns = ldap_explode_dn( dn, 0 )) == NULL ) {
  994. return NULL;
  995. }
  996. for ( i = 1; rdns[ i ] != NULL; i++ ) {
  997. strcat( dnp, rdns[ i ] );
  998. strcat( dnp, "," );
  999. }
  1000. /* Get rid of the trailing "," we just appended */
  1001. dnp[ strlen( dnp ) - 1 ] = '\0';
  1002. ldap_value_free( rdns );
  1003. return( dnp );
  1004. }
  1005. /*
  1006. * Return 1 if dn1 is the immediate ancestor of dn2, 0 otherwise.
  1007. */
  1008. int
  1009. dsgw_is_dnparent( char *dn1, char *dn2 )
  1010. {
  1011. char *dnp;
  1012. int rc;
  1013. /* A null or zero-length DN cannot have a parent */
  1014. if ( dn2 == NULL || strlen( dn2 ) == 0 ) {
  1015. return 0;
  1016. }
  1017. dnp = dsgw_dn_parent( dn2 );
  1018. rc = dsgw_dn_cmp( dn1, dnp );
  1019. free( dnp );
  1020. return rc;
  1021. }
  1022. /*
  1023. * return malloc'd array of RDN attribute value pairs
  1024. * each element of the array is a string that looks like: TAG=VALUE
  1025. * this is used to extract values from the RDN when a new entry is added
  1026. */
  1027. char **
  1028. dsgw_rdn_values( char *dn )
  1029. {
  1030. char **rdns, **rdncomps, *val;
  1031. int i;
  1032. if (( rdns = ldap_explode_dn( dn, 0 )) == NULL ) {
  1033. return( NULL );
  1034. }
  1035. rdncomps = ldap_explode_rdn( rdns[0], 0 );
  1036. ldap_value_free( rdns );
  1037. if ( rdncomps == NULL ) {
  1038. return( NULL );
  1039. }
  1040. for ( i = 0; rdncomps[ i ] != NULL; ++i ) {
  1041. if (( val = strchr( rdncomps[ i ], '=' )) == NULL ) {
  1042. ldap_value_free( rdncomps );
  1043. return( NULL );
  1044. }
  1045. ++val;
  1046. strcpy_special_undo( val, val ); /* undo in place */
  1047. }
  1048. return( rdncomps );
  1049. }
  1050. /*
  1051. * the following routine was lifted from servers/slapd/ava.c
  1052. * it removes special quoting, etc. from values that appear in an LDAP DN
  1053. */
  1054. static void
  1055. strcpy_special_undo( char *d, char *s )
  1056. {
  1057. int quote;
  1058. quote = 0;
  1059. if ( *s == '"' ) {
  1060. s++;
  1061. quote = 1;
  1062. }
  1063. for ( ; *s; LDAP_UTF8INC(s)) {
  1064. switch ( *s ) {
  1065. case '"':
  1066. break;
  1067. case '\\':
  1068. s++;
  1069. /* FALL */
  1070. default:
  1071. d += LDAP_UTF8COPY (d, s);
  1072. break;
  1073. }
  1074. }
  1075. *d = '\0'; LDAP_UTF8DEC(d);
  1076. if ( quote && *d == '"' ) {
  1077. *d = '\0';
  1078. }
  1079. }
  1080. static char *
  1081. uid2dn( LDAP *ld, char *uid, char *base, int *ldaprc, char **lderrtxtp,
  1082. char **errsp )
  1083. {
  1084. char *attrs[] = { "objectclass", NULL };
  1085. char filtbuf[ 85 ]; /* max of 80 char. uid + "uid=" + zero terminator */
  1086. int rc, count;
  1087. LDAPMessage *result;
  1088. LDAPMessage *e;
  1089. char *dn;
  1090. *ldaprc = LDAP_SUCCESS; /* optimistic */
  1091. *errsp = *lderrtxtp = NULL;
  1092. if ( ld == NULL || uid == NULL || strlen( uid ) > 80 ) {
  1093. *errsp = XP_GetClientStr(DBT_invalidUserIdOrNullLdapHandle_);
  1094. return NULL;
  1095. }
  1096. PR_snprintf( filtbuf, 85, "uid=%s", uid );
  1097. if (( rc = ldap_search_s( ld, base, LDAP_SCOPE_SUBTREE, filtbuf,
  1098. attrs, 1, &result )) != LDAP_SUCCESS ) {
  1099. *ldaprc = rc;
  1100. (void)ldap_get_lderrno( ld, NULL, lderrtxtp );
  1101. return NULL;
  1102. }
  1103. if (( count = ldap_count_entries( ld, result )) != 1 ) {
  1104. /* Search either returned no entries, or more than one entry */
  1105. ldap_msgfree( result );
  1106. if ( count == 0 ) {
  1107. *errsp = XP_GetClientStr(DBT_noMatchForUserId_);
  1108. } else {
  1109. *errsp = XP_GetClientStr(DBT_moreThanOneMatchForUserId_);
  1110. }
  1111. return NULL;
  1112. }
  1113. dn = NULL;
  1114. if (( e = ldap_first_entry( ld, result )) == NULL ||
  1115. ( dn = ldap_get_dn( ld, e )) == NULL ) {
  1116. *ldaprc = ldap_get_lderrno( ld, NULL, NULL );
  1117. }
  1118. ldap_msgfree( result );
  1119. return( dn );
  1120. }
  1121. /*
  1122. * Emit an HTML "SELECT" object that contains all the o's and ou's that
  1123. * are underneath our default searchbase. If there are none other than
  1124. * the searchbase, we emit a hidden HTML TEXT object that contains the
  1125. * searchbase and the "prefix" and "suffix" are not used. The values for
  1126. * the SELECT options and for the TEXT object are all escaped DNs.
  1127. *
  1128. * Location popup directives look like this:
  1129. * <-- DS_LOCATIONPOPUP "name=VARNAME" "prefix=PREFIX" "suffix=SUFFIX" -->
  1130. *
  1131. * If "prefix" and/or "suffix" are omitted, they default to "".
  1132. * If "name" is omitted it defaults to "base".
  1133. *
  1134. * If there are "location" directives in the dsgw.conf file, we use those
  1135. * instead of actually searching the directory.
  1136. */
  1137. void
  1138. dsgw_emit_location_popup( LDAP *ld, int argc, char **argv, int erropts )
  1139. {
  1140. char line[BIG_LINE];
  1141. char *varname, *prefix, *suffix, *rootname, *dn;
  1142. int i, count, did_init_ldap;
  1143. LDAPMessage *res, *e;
  1144. if (( varname = get_arg_by_name( "name", argc, argv )) == NULL ) {
  1145. varname = "base";
  1146. }
  1147. if (( prefix = get_arg_by_name( "prefix", argc, argv )) == NULL ) {
  1148. prefix = "";
  1149. }
  1150. if (( suffix = get_arg_by_name( "suffix", argc, argv )) == NULL ) {
  1151. suffix = "";
  1152. }
  1153. rootname = get_arg_by_name( "rootname", argc, argv );
  1154. did_init_ldap = 0;
  1155. res = NULL;
  1156. if ( gc->gc_newentryloccount > 0 ) {
  1157. count = gc->gc_newentryloccount;
  1158. } else {
  1159. char *attrs[ 3 ];
  1160. int rc;
  1161. if ( ld == NULL ) {
  1162. (void)dsgw_init_ldap( &ld, NULL, 0, 0 );
  1163. did_init_ldap = 1;
  1164. }
  1165. attrs[ 0 ] = "o";
  1166. attrs[ 1 ] = "ou";
  1167. attrs[ 2 ] = NULL;
  1168. rc = ldap_search_s( ld, gc->gc_ldapsearchbase, LDAP_SCOPE_SUBTREE,
  1169. "(|(objectclass=organization)(objectclass=organizationalunit))",
  1170. attrs, 1, &res );
  1171. if ( rc != LDAP_SUCCESS || res == NULL ) {
  1172. dsgw_ldap_error( ld, erropts );
  1173. return;
  1174. }
  1175. count = ldap_count_entries( ld, res );
  1176. if ( gc->gc_ldapsearchbase == NULL || *gc->gc_ldapsearchbase == '\0' ) {
  1177. ++count; /* include base DN even if it is "" */
  1178. } else {
  1179. /*
  1180. * check to see if search base was one of the entries returned
  1181. * we want to always list the base entry, so we need to check
  1182. */
  1183. for ( e = ldap_first_entry( ld, res ); e != NULL;
  1184. e = ldap_next_entry( ld, e )) {
  1185. if (( dn = ldap_get_dn( ld, e )) == NULL ) {
  1186. dsgw_ldap_error( ld, erropts );
  1187. ldap_msgfree( res );
  1188. return;
  1189. }
  1190. rc = dsgw_dn_cmp( dn, gc->gc_ldapsearchbase );
  1191. free( dn );
  1192. if ( rc ) { /* base DN was returned */
  1193. break;
  1194. }
  1195. }
  1196. if ( e == NULL ) {
  1197. ++count; /* include base DN even if was not returned */
  1198. }
  1199. }
  1200. }
  1201. if ( count > 1 ) {
  1202. util_snprintf( line, BIG_LINE, "%s\n<SELECT NAME=\"%s\">\n",
  1203. prefix, varname );
  1204. } else {
  1205. util_snprintf( line, BIG_LINE, "<INPUT TYPE=\"hidden\" NAME=\"%s\" ",
  1206. varname );
  1207. }
  1208. dsgw_emits( line );
  1209. if ( gc->gc_newentryloccount > 0 ) {
  1210. for ( i = 0; i < gc->gc_newentryloccount; ++i ) {
  1211. emit_one_loc_dn( gc->gc_newentrylocs[ i ].dsloc_dnsuffix,
  1212. gc->gc_newentrylocs[i].dsloc_fullname, rootname,
  1213. ( count < 2 ));
  1214. }
  1215. } else {
  1216. /* always include the base dn first */
  1217. emit_one_loc_dn( gc->gc_ldapsearchbase, NULL, rootname, ( count < 2 ));
  1218. /* XXXmcs it would be nice to do a more intelligent sort here */
  1219. #ifdef DSGW_DEBUG
  1220. dsgw_log( "dsgw_emit_location_popup: ldap_sort_entries(NULL)\n" );
  1221. #endif
  1222. ldap_sort_entries( ld, &res, NULL, dsgw_strcmp (CASE_INSENSITIVE));
  1223. for ( e = ldap_first_entry( ld, res ); e != NULL;
  1224. e = ldap_next_entry( ld, e )) {
  1225. if (( dn = ldap_get_dn( ld, e )) == NULL ) {
  1226. dsgw_ldap_error( ld, erropts );
  1227. ldap_msgfree( res );
  1228. return;
  1229. }
  1230. if ( !dsgw_dn_cmp( dn, gc->gc_ldapsearchbase )) {
  1231. emit_one_loc_dn( dn, NULL, rootname, ( count < 2 ));
  1232. }
  1233. free( dn );
  1234. }
  1235. }
  1236. if ( count > 1 ) {
  1237. util_snprintf( line, BIG_LINE, "</SELECT>\n%s\n", suffix );
  1238. dsgw_emits( line );
  1239. }
  1240. if ( res != NULL ) {
  1241. ldap_msgfree( res );
  1242. }
  1243. if ( did_init_ldap ) {
  1244. ldap_unbind( ld );
  1245. }
  1246. }
  1247. static void
  1248. emit_one_loc_dn( char *dn, char *friendlyname, char *rootname, int only_one )
  1249. {
  1250. char *escapeddn, **rdns, line[ BIG_LINE ];
  1251. rdns = NULL;
  1252. escapeddn = dsgw_strdup_escaped( dn );
  1253. if ( !only_one ) {
  1254. dsgw_emits( "<OPTION" );
  1255. }
  1256. if ( friendlyname == NULL ) { /* use first component of DN */
  1257. if ( *dn == '\0' ) {
  1258. friendlyname = ( rootname == NULL ? XP_GetClientStr(DBT_theEntireDirectory_)
  1259. : rootname );
  1260. } else if (( rdns = ldap_explode_dn( dn, 1 )) == NULL
  1261. || rdns[ 0 ] == NULL ) {
  1262. friendlyname = dn;
  1263. } else {
  1264. friendlyname = rdns[ 0 ];
  1265. }
  1266. }
  1267. util_snprintf( line, BIG_LINE, " VALUE=\"%s\">%s\n", escapeddn,
  1268. only_one ? "" : friendlyname );
  1269. free( escapeddn );
  1270. if ( rdns != NULL ) {
  1271. ldap_value_free( rdns );
  1272. }
  1273. dsgw_emits( line );
  1274. }
  1275. /*
  1276. * Return a MIME document that contains a single value.
  1277. * XXX: does this really belong in ldaputil.c?
  1278. */
  1279. static void
  1280. return_one_attr( LDAP *ld, LDAPMessage *entry, char *attrtype, char *mimetype,
  1281. int valindex )
  1282. {
  1283. char *val;
  1284. struct berval **bvals;
  1285. unsigned long vlen;
  1286. if (( bvals = ldap_get_values_len( ld, entry, attrtype )) == NULL ) {
  1287. dsgw_error( DSGW_ERR_NOATTRVALUE, attrtype, DSGW_ERROPT_EXIT, 0, NULL );
  1288. }
  1289. if ( valindex > ldap_count_values_len( bvals )) {
  1290. dsgw_error( DSGW_ERR_NOATTRVALUE, attrtype, DSGW_ERROPT_EXIT, 0, NULL );
  1291. }
  1292. val = bvals[ valindex ]->bv_val;
  1293. vlen = bvals[ valindex ]->bv_len;
  1294. fprintf( stdout, "Content-Type: %s\n", mimetype );
  1295. fprintf( stdout, "Content-Length: %ld\n\n", vlen );
  1296. #ifdef XP_WIN32
  1297. /* flush any data on stdout before changing the mode */
  1298. fflush( stdout );
  1299. /* set the mode to binary
  1300. so windows doesn't replace with carriage
  1301. return line feed and mess everything up
  1302. */
  1303. _setmode( _fileno( stdout ), _O_BINARY );
  1304. #endif
  1305. fwrite( val, vlen, 1, stdout );
  1306. #ifdef XP_WIN32
  1307. /* flush any remaining binary data */
  1308. fflush( stdout );
  1309. /* set the mode back to text */
  1310. _setmode( _fileno( stdout ), _O_TEXT );
  1311. #endif
  1312. ldap_value_free_len( bvals );
  1313. free( attrtype );
  1314. }
  1315. /*
  1316. * The general format of attrtype is:
  1317. * <attrtype> [ &<mimetype> ] [ &<valindex> ]
  1318. * This routine breaks it up. Callers should free( *attrtypep ) after they
  1319. * are done using attrtypep and mimetypep.
  1320. */
  1321. static void
  1322. break_up_one_attr( char *attr, char **attrtypep, char **mimetypep,
  1323. int *valindexp )
  1324. {
  1325. char *p;
  1326. *attrtypep = dsgw_ch_strdup( attr );
  1327. *mimetypep = "text/plain"; /* default */
  1328. *valindexp = 0; /* default: retrieve first value */
  1329. if (( p = strchr( *attrtypep, '&' )) != NULL ) {
  1330. *p++ = '\0';
  1331. if ( *p != '\0' ) {
  1332. *mimetypep = p;
  1333. if (( p = strchr( *mimetypep, '&' )) != NULL ) {
  1334. *p++ = '\0';
  1335. *valindexp = atoi( p );
  1336. }
  1337. }
  1338. }
  1339. }