ldaputil.c 43 KB

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