ldapauth.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131
  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. * ldapauth.cpp: Implements LDAP integration in the web server.
  40. *
  41. * Nitin More, John Kristian
  42. */
  43. /* #define DBG_PRINT */
  44. #include <stdio.h> /* for BUFSIZ */
  45. #include <string.h> /* for strncpy, strcat */
  46. #include <ldap.h>
  47. #include <prprf.h>
  48. #include <ldaputil/certmap.h>
  49. #include <ldaputil/errors.h>
  50. #include <ldaputil/ldapauth.h>
  51. #include <ldaputili.h>
  52. /* If we are not interested in the returned attributes, just ask for one
  53. * attribute in the call to ldap_search. Also don't ask for the attribute
  54. * value -- just the attr.
  55. */
  56. static const char *default_search_attrs[] = { "c" , 0 };
  57. static int default_search_attrsonly = 1;
  58. /*
  59. * ldapu_find
  60. * Description:
  61. * Caller should free res if it is not NULL.
  62. * Arguments:
  63. * ld Pointer to LDAP (assumes connection has been
  64. * established and the client has called the
  65. * appropriate bind routine)
  66. * base basedn (where to start the search)
  67. * scope scope for the search. One of
  68. * LDAP_SCOPE_SUBTREE, LDAP_SCOPE_ONELEVEL, and
  69. * LDAP_SCOPE_BASE
  70. * filter LDAP filter
  71. * attrs A NULL-terminated array of strings indicating which
  72. * attributes to return for each matching entry. Passing
  73. * NULL for this parameter causes all available
  74. * attributes to be retrieved.
  75. * attrsonly A boolean value that should be zero if both attribute
  76. * types and values are to be returned, non-zero if only
  77. * types are wanted.
  78. * res A result parameter which will contain the results of
  79. * the search upon completion of the call.
  80. * Return Values:
  81. * LDAPU_SUCCESS if entry is found
  82. * LDAPU_FAILED if entry is not found
  83. * <rv> if error, where <rv> can be passed to
  84. * ldap_err2string to get an error string.
  85. */
  86. int ldapu_find (LDAP *ld, const char *base, int scope,
  87. const char *filter, const char **attrs,
  88. int attrsonly, LDAPMessage **res)
  89. {
  90. int retval;
  91. #ifdef USE_THIS_CODE /* ASYNCHRONOUS */
  92. int msgid;
  93. #endif
  94. int numEntries;
  95. *res = 0;
  96. /* If base is NULL set it to null string */
  97. if (!base) {
  98. DBG_PRINT1("ldapu_find: basedn is missing -- assuming null string\n");
  99. base = "";
  100. }
  101. if (!filter || !*filter) {
  102. DBG_PRINT1("ldapu_find: filter is missing -- assuming objectclass=*\n");
  103. filter = ldapu_strings[LDAPU_STR_FILTER_DEFAULT];
  104. }
  105. DBG_PRINT2("\tbase:\t\"%s\"\n", base);
  106. DBG_PRINT2("\tfilter:\t\"%s\"\n", filter ? filter : "<NULL>");
  107. DBG_PRINT2("\tscope:\t\"%s\"\n",
  108. (scope == LDAP_SCOPE_SUBTREE ? "LDAP_SCOPE_SUBTREE"
  109. : (scope == LDAP_SCOPE_ONELEVEL ? "LDAP_SCOPE_ONELEVEL"
  110. : "LDAP_SCOPE_BASE")));
  111. retval = ldapu_search_s(ld, base, scope, filter, (char **)attrs,
  112. attrsonly, res);
  113. if (retval != LDAP_SUCCESS)
  114. {
  115. /* retval = ldap_result2error(ld, *res, 0); */
  116. DBG_PRINT2("ldapu_search_s: %s\n", ldapu_err2string(retval));
  117. return(retval);
  118. }
  119. numEntries = ldapu_count_entries(ld, *res);
  120. if (numEntries == 1) {
  121. /* success */
  122. return LDAPU_SUCCESS;
  123. }
  124. else if (numEntries == 0) {
  125. /* not found -- but not an error */
  126. DBG_PRINT1("ldapu_search_s: Entry not found\n");
  127. return LDAPU_FAILED;
  128. }
  129. else if (numEntries > 0) {
  130. /* Found more than one entry! */
  131. DBG_PRINT1("ldapu_search_s: Found more than one entry\n");
  132. return LDAPU_ERR_MULTIPLE_MATCHES;
  133. }
  134. else {
  135. /* should never get here */
  136. DBG_PRINT1("ldapu_search_s: should never reach here\n");
  137. ldapu_msgfree(ld, *res);
  138. return LDAP_OPERATIONS_ERROR;
  139. }
  140. }
  141. /* Search function for the cases where base = "" = NULL suffix, that is, search to
  142. * be performed on the entire DIT tree.
  143. * We actually do various searches taking a naming context at a time as the base for
  144. * the search. */
  145. int ldapu_find_entire_tree (LDAP *ld, int scope,
  146. const char *filter, const char **attrs,
  147. int attrsonly, LDAPMessage ***res)
  148. {
  149. int retval = LDAPU_FAILED;
  150. int rv,i, num_namingcontexts;
  151. LDAPMessage *result_entry, *result = NULL;
  152. const char *suffix_attr[2] = {"namingcontexts", NULL};
  153. /* these are private suffixes that may contain pseudo users
  154. e.g. replication manager that may have certs */
  155. int num_private_suffix = 1;
  156. const char *private_suffix_list[2] = {"cn=config", NULL};
  157. char **suffix_list, **suffix = NULL;
  158. rv = ldapu_find(ld, "",LDAP_SCOPE_BASE, "objectclass=*", suffix_attr, 0, &result);
  159. if (rv != LDAP_SUCCESS) {
  160. if (result) ldapu_msgfree(ld, result);
  161. return rv;
  162. }
  163. result_entry = ldapu_first_entry(ld, result);
  164. suffix = ldapu_get_values(ld, result_entry, suffix_attr[0]);
  165. suffix_list = suffix;
  166. num_namingcontexts = ldap_count_values(suffix);
  167. /* add private suffixes to our list of suffixes to search */
  168. if (num_private_suffix) {
  169. suffix_list = ldapu_realloc(suffix_list,
  170. sizeof(char *)*(num_namingcontexts+num_private_suffix+1));
  171. if (!suffix_list) {
  172. if (result) {
  173. ldapu_msgfree(ld, result);
  174. }
  175. retval = LDAPU_FAILED;
  176. return retval;
  177. }
  178. for (i = num_namingcontexts; i < (num_namingcontexts+num_private_suffix); ++i) {
  179. suffix_list[i] = strdup(private_suffix_list[i-num_namingcontexts]);
  180. }
  181. suffix_list[i] = NULL;
  182. num_namingcontexts += num_private_suffix;
  183. suffix = suffix_list;
  184. }
  185. if (result) ldapu_msgfree(ld, result);
  186. result = 0;
  187. i = 0;
  188. /* ugaston - the caller function must remember to free the memory allocated here */
  189. *res = (LDAPMessage **) ldapu_malloc((num_namingcontexts + 1) * sizeof(LDAPMessage *));
  190. while (suffix && *suffix) {
  191. rv = ldapu_find(ld, *suffix, scope, filter, attrs, attrsonly, &result);
  192. if (scope == LDAP_SCOPE_BASE && rv == LDAP_SUCCESS) {
  193. retval = rv;
  194. (*res)[i++] = result;
  195. break;
  196. }
  197. switch (rv) {
  198. case LDAP_SUCCESS:
  199. if (retval == LDAP_SUCCESS) {
  200. retval = LDAPU_ERR_MULTIPLE_MATCHES;
  201. (*res)[i++] = result;
  202. break;
  203. }
  204. case LDAPU_ERR_MULTIPLE_MATCHES:
  205. retval = rv;
  206. (*res)[i++] = result;
  207. break;
  208. default:
  209. if (retval != LDAP_SUCCESS && retval != LDAPU_ERR_MULTIPLE_MATCHES) {
  210. retval = rv;
  211. }
  212. if (result) ldapu_msgfree(ld, result);
  213. result = 0;
  214. break;
  215. }
  216. suffix++;
  217. }
  218. (*res)[i] = NULL;
  219. ldapu_value_free(ld, suffix_list);
  220. return retval;
  221. }
  222. /*
  223. * ldapu_find_uid_attrs
  224. * Description:
  225. * Maps the given uid to a user dn. Caller should free res if it is not
  226. * NULL. Accepts the attrs & attrsonly args.
  227. * Arguments:
  228. * ld Pointer to LDAP (assumes connection has been
  229. * established and the client has called the
  230. * appropriate bind routine)
  231. * uid User's name
  232. * base basedn (where to start the search)
  233. * attrs list of attributes to retrieve
  234. * attrsonly flag indicating if attr values are to be retrieved
  235. * res A result parameter which will contain the results of
  236. * the search upon completion of the call.
  237. * Return Values:
  238. * LDAPU_SUCCESS if entry is found
  239. * LDAPU_FAILED if entry is not found
  240. * <rv> if error, where <rv> can be passed to
  241. * ldap_err2string to get an error string.
  242. */
  243. int ldapu_find_uid_attrs (LDAP *ld, const char *uid, const char *base,
  244. const char **attrs, int attrsonly,
  245. LDAPMessage **res)
  246. {
  247. int scope = LDAP_SCOPE_SUBTREE;
  248. char filter[ BUFSIZ ];
  249. int retval;
  250. /* setup filter as (uid=<uid>) */
  251. PR_snprintf(filter, sizeof(filter), ldapu_strings[LDAPU_STR_FILTER_USER], uid);
  252. retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, res);
  253. return retval;
  254. }
  255. /*
  256. * ldapu_find_uid
  257. * Description:
  258. * Maps the given uid to a user dn. Caller should free res if it is not
  259. * NULL.
  260. * Arguments:
  261. * ld Pointer to LDAP (assumes connection has been
  262. * established and the client has called the
  263. * appropriate bind routine)
  264. * uid User's name
  265. * base basedn (where to start the search)
  266. * res A result parameter which will contain the results of
  267. * the search upon completion of the call.
  268. * Return Values:
  269. * LDAPU_SUCCESS if entry is found
  270. * LDAPU_FAILED if entry is not found
  271. * <rv> if error, where <rv> can be passed to
  272. * ldap_err2string to get an error string.
  273. */
  274. int ldapu_find_uid (LDAP *ld, const char *uid, const char *base,
  275. LDAPMessage **res)
  276. {
  277. const char **attrs = 0; /* get all attributes ... */
  278. int attrsonly = 0; /* ... and their values */
  279. int retval;
  280. retval = ldapu_find_uid_attrs(ld, uid, base, attrs, attrsonly, res);
  281. return retval;
  282. }
  283. /*
  284. * ldapu_find_userdn
  285. * Description:
  286. * Maps the given uid to a user dn. Caller should free dn if it is not
  287. * NULL.
  288. * Arguments:
  289. * ld Pointer to LDAP (assumes connection has been
  290. * established and the client has called the
  291. * appropriate bind routine)
  292. * uid User's name
  293. * base basedn (where to start the search)
  294. * dn user dn
  295. * Return Values:
  296. * LDAPU_SUCCESS if entry is found
  297. * LDAPU_FAILED if entry is not found
  298. * <rv> if error, where <rv> can be passed to
  299. * ldap_err2string to get an error string.
  300. */
  301. int ldapu_find_userdn (LDAP *ld, const char *uid, const char *base,
  302. char **dn)
  303. {
  304. LDAPMessage *res = 0;
  305. int retval;
  306. retval = ldapu_find_uid_attrs(ld, uid, base, default_search_attrs,
  307. default_search_attrsonly, &res);
  308. if (retval == LDAPU_SUCCESS) {
  309. LDAPMessage *entry;
  310. entry = ldapu_first_entry(ld, res);
  311. *dn = ldapu_get_dn(ld, entry);
  312. }
  313. else {
  314. *dn = 0;
  315. }
  316. if (res) ldapu_msgfree(ld, res);
  317. return retval;
  318. }
  319. /*
  320. * ldapu_find_group_attrs
  321. * Description:
  322. * Maps the given groupid to a group dn. Caller should free res if it is
  323. * not NULL. Accepts the attrs & attrsonly args.
  324. * Arguments:
  325. * ld Pointer to LDAP (assumes connection has been
  326. * established and the client has called the
  327. * appropriate bind routine)
  328. * groupid Groups's name
  329. * base basedn (where to start the search)
  330. * attrs list of attributes to retrieve
  331. * attrsonly flag indicating if attr values are to be retrieved
  332. * res A result parameter which will contain the results of
  333. * the search upon completion of the call.
  334. * Return Values:
  335. * LDAPU_SUCCESS if entry is found
  336. * LDAPU_FAILED if entry is not found
  337. * <rv> if error, where <rv> can be passed to
  338. * ldap_err2string to get an error string.
  339. */
  340. int ldapu_find_group_attrs (LDAP *ld, const char *groupid,
  341. const char *base, const char **attrs,
  342. int attrsonly, LDAPMessage **res)
  343. {
  344. int scope = LDAP_SCOPE_SUBTREE;
  345. char filter[ BUFSIZ ];
  346. int retval;
  347. /* setup the filter */
  348. PR_snprintf(filter, sizeof(filter),
  349. ldapu_strings[LDAPU_STR_FILTER_GROUP],
  350. groupid);
  351. retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, res);
  352. return retval;
  353. }
  354. /*
  355. * ldapu_find_group
  356. * Description:
  357. * Maps the given groupid to a group dn. Caller should free res if it is
  358. * not NULL.
  359. * Arguments:
  360. * ld Pointer to LDAP (assumes connection has been
  361. * established and the client has called the
  362. * appropriate bind routine)
  363. * groupid Groups's name
  364. * base basedn (where to start the search)
  365. * res A result parameter which will contain the results of
  366. * the search upon completion of the call.
  367. * Return Values:
  368. * LDAPU_SUCCESS if entry is found
  369. * LDAPU_FAILED if entry is not found
  370. * <rv> if error, where <rv> can be passed to
  371. * ldap_err2string to get an error string.
  372. */
  373. int ldapu_find_group (LDAP *ld, const char *groupid, const char *base,
  374. LDAPMessage **res)
  375. {
  376. const char **attrs = 0; /* get all attributes ... */
  377. int attrsonly = 0; /* ... and their values */
  378. int retval;
  379. retval = ldapu_find_group_attrs (ld, groupid, base, attrs, attrsonly, res);
  380. return retval;
  381. }
  382. /*
  383. * ldapu_find_groupdn
  384. * Description:
  385. * Maps the given groupid to a group dn. Caller should free dn if it is
  386. * not NULL.
  387. * Arguments:
  388. * ld Pointer to LDAP (assumes connection has been
  389. * established and the client has called the
  390. * appropriate bind routine)
  391. * groupid Groups's name
  392. * base basedn (where to start the search)
  393. * dn group dn
  394. * Return Values:
  395. * LDAPU_SUCCESS if entry is found
  396. * LDAPU_FAILED if entry is not found
  397. * <rv> if error, where <rv> can be passed to
  398. * ldap_err2string to get an error string.
  399. */
  400. int ldapu_find_groupdn (LDAP *ld, const char *groupid, const char *base,
  401. char **dn)
  402. {
  403. LDAPMessage *res = 0;
  404. int retval;
  405. retval = ldapu_find_group_attrs(ld, groupid, base, default_search_attrs,
  406. default_search_attrsonly, &res);
  407. if (retval == LDAPU_SUCCESS) {
  408. LDAPMessage *entry;
  409. /* get ldap entry */
  410. entry = ldapu_first_entry(ld, res);
  411. *dn = ldapu_get_dn(ld, entry);
  412. }
  413. else {
  414. *dn = 0;
  415. }
  416. if (res) ldapu_msgfree(ld, res);
  417. return retval;
  418. }
  419. /*
  420. * continuable_err
  421. * Description:
  422. * Returns true for benign errors (i.e. errors for which recursive
  423. * search can continue.
  424. * Return Values:
  425. * 0 (zero) - if not a benign error
  426. * 1 - if a benign error -- search can continue.
  427. */
  428. static int continuable_err (int err)
  429. {
  430. return (err == LDAPU_FAILED);
  431. }
  432. int ldapu_auth_udn_gdn_recurse (LDAP *ld, const char *userdn,
  433. const char *groupdn, const char *base,
  434. int recurse_cnt)
  435. {
  436. char filter[ BUFSIZ ];
  437. const char **attrs = default_search_attrs;
  438. int attrsonly = default_search_attrsonly;
  439. LDAPMessage *res = 0;
  440. int retval;
  441. char member_filter[ BUFSIZ ];
  442. if (recurse_cnt >= 30)
  443. return LDAPU_ERR_CIRCULAR_GROUPS;
  444. /* setup the filter */
  445. PR_snprintf(member_filter, sizeof(member_filter), ldapu_strings[LDAPU_STR_FILTER_MEMBER], userdn, userdn);
  446. retval = ldapu_find(ld, groupdn, LDAP_SCOPE_BASE, member_filter, attrs,
  447. attrsonly, &res);
  448. if (res) ldap_msgfree(res);
  449. if (retval != LDAPU_SUCCESS && continuable_err(retval)) {
  450. LDAPMessage *entry;
  451. DBG_PRINT2("Find parent groups of \"%s\"\n", userdn);
  452. /* Modify the filter to include the objectclass check */
  453. PR_snprintf(filter, sizeof(filter), ldapu_strings[LDAPU_STR_FILTER_MEMBER_RECURSE],
  454. member_filter);
  455. retval = ldapu_find(ld, base, LDAP_SCOPE_SUBTREE, filter,
  456. attrs, attrsonly, &res);
  457. if (retval == LDAPU_SUCCESS || retval == LDAPU_ERR_MULTIPLE_MATCHES) {
  458. /* Found at least one group the userdn is member of */
  459. if (!res) {
  460. /* this should never happen */
  461. retval = LDAPU_ERR_EMPTY_LDAP_RESULT;
  462. }
  463. else {
  464. retval = LDAPU_ERR_MISSING_RES_ENTRY;
  465. for (entry = ldap_first_entry(ld, res); entry != NULL;
  466. entry = ldap_next_entry(ld, entry))
  467. {
  468. char *dn = ldap_get_dn(ld, entry);
  469. retval = ldapu_auth_udn_gdn_recurse(ld, dn, groupdn,
  470. base, recurse_cnt+1);
  471. ldap_memfree(dn);
  472. if (retval == LDAPU_SUCCESS || !continuable_err(retval)) {
  473. break;
  474. }
  475. }
  476. }
  477. }
  478. if (res) ldap_msgfree(res);
  479. }
  480. return retval;
  481. }
  482. /*
  483. * ldapu_auth_userdn_groupdn:
  484. * Description:
  485. * Checks if the user (userdn) belongs to the given group (groupdn).
  486. * Arguments:
  487. * ld Pointer to LDAP (assumes connection has been
  488. * established and the client has called the
  489. * appropriate bind routine)
  490. * userdn User's full DN -- actually it could be a group
  491. * dn to check subgroup membership.
  492. * groupdn Group's full DN
  493. * Return Values: (same as ldapu_find)
  494. * LDAPU_SUCCESS if user is member of the group
  495. * LDAPU_FAILED if user is not a member of the group
  496. * <rv> if error, where <rv> can be passed to
  497. * ldap_err2string to get an error string.
  498. */
  499. int ldapu_auth_userdn_groupdn (LDAP *ld, const char *userdn,
  500. const char *groupdn, const char *base)
  501. {
  502. return ldapu_auth_udn_gdn_recurse(ld, userdn, groupdn, base, 0);
  503. }
  504. /*
  505. * ldapu_auth_uid_groupdn:
  506. * Description:
  507. * Similar to ldapu_auth_userdn_groupdn but first maps the uid to a
  508. * full user DN before calling ldapu_auth_userdn_groupdn.
  509. * Arguments:
  510. * ld Pointer to LDAP (assumes connection has been
  511. * established and the client has called the
  512. * appropriate bind routine)
  513. * uid User's login name
  514. * groupdn Group's full DN
  515. * base basedn (where to start the search)
  516. * Return Values: (same as ldapu_find)
  517. * LDAPU_SUCCESS if user is member of the group
  518. * LDAPU_FAILED if user is not a member of the group
  519. * <rv> if error, where <rv> can be passed to
  520. * ldap_err2string to get an error string.
  521. */
  522. int ldapu_auth_uid_groupdn (LDAP *ld, const char *uid, const char *groupdn,
  523. const char *base)
  524. {
  525. int retval;
  526. char *dn;
  527. /* First find userdn for the given uid and
  528. then call ldapu_auth_userdn_groupdn */
  529. retval = ldapu_find_userdn(ld, uid, base, &dn);
  530. if (retval == LDAPU_SUCCESS) {
  531. retval = ldapu_auth_userdn_groupdn(ld, dn, groupdn, base);
  532. ldap_memfree(dn);
  533. }
  534. return retval;
  535. }
  536. /*
  537. * ldapu_auth_uid_groupid:
  538. * Description:
  539. * Similar to ldapu_auth_uid_groupdn but first maps the groupid to a
  540. * full group DN before calling ldapu_auth_uid_groupdn.
  541. * Arguments:
  542. * ld Pointer to LDAP (assumes connection has been
  543. * established and the client has called the
  544. * appropriate bind routine)
  545. * uid User's login name
  546. * groupid Group's name
  547. * base basedn (where to start the search)
  548. * Return Values: (same as ldapu_find)
  549. * LDAPU_SUCCESS if user is member of the group
  550. * LDAPU_FAILED if user is not a member of the group
  551. * <rv> if error, where <rv> can be passed to
  552. * ldap_err2string to get an error string.
  553. */
  554. int ldapu_auth_uid_groupid (LDAP *ld, const char *uid,
  555. const char *groupid, const char *base)
  556. {
  557. int retval;
  558. char *dn;
  559. /* First find groupdn for the given groupid and
  560. then call ldapu_auth_uid_groupdn */
  561. retval = ldapu_find_groupdn(ld, groupid, base, &dn);
  562. if (retval == LDAPU_SUCCESS) {
  563. retval = ldapu_auth_uid_groupdn(ld, uid, dn, base);
  564. ldapu_memfree(ld, dn);
  565. }
  566. return retval;
  567. }
  568. /*
  569. * ldapu_auth_userdn_groupid:
  570. * Description:
  571. * Similar to ldapu_auth_userdn_groupdn but first maps the groupid to a
  572. * full group DN before calling ldapu_auth_userdn_groupdn.
  573. * Arguments:
  574. * ld Pointer to LDAP (assumes connection has been
  575. * established and the client has called the
  576. * appropriate bind routine)
  577. * userdn User's full DN
  578. * groupid Group's name
  579. * base basedn (where to start the search)
  580. * Return Values: (same as ldapu_find)
  581. * LDAPU_SUCCESS if user is member of the group
  582. * LDAPU_FAILED if user is not a member of the group
  583. * <rv> if error, where <rv> can be passed to
  584. * ldap_err2string to get an error string.
  585. */
  586. int ldapu_auth_userdn_groupid (LDAP *ld, const char *userdn,
  587. const char *groupid, const char *base)
  588. {
  589. int retval;
  590. char *groupdn;
  591. /* First find groupdn for the given groupid and
  592. then call ldapu_auth_userdn_groupdn */
  593. retval = ldapu_find_groupdn(ld, groupid, base, &groupdn);
  594. if (retval == LDAPU_SUCCESS) {
  595. retval = ldapu_auth_userdn_groupdn(ld, userdn, groupdn, base);
  596. ldap_memfree(groupdn);
  597. }
  598. return retval;
  599. }
  600. LDAPUStr_t *ldapu_str_alloc (const int size)
  601. {
  602. LDAPUStr_t *lstr = (LDAPUStr_t *)ldapu_malloc(sizeof(LDAPUStr_t));
  603. if (!lstr) return 0;
  604. lstr->size = size < 0 ? 1024 : size;
  605. lstr->str = (char *)ldapu_malloc(lstr->size*sizeof(char));
  606. lstr->len = 0;
  607. lstr->str[lstr->len] = 0;
  608. return lstr;
  609. }
  610. void ldapu_str_free (LDAPUStr_t *lstr)
  611. {
  612. if (lstr) {
  613. if (lstr->str) ldapu_free(lstr->str);
  614. ldapu_free((void *)lstr);
  615. }
  616. }
  617. int ldapu_str_append(LDAPUStr_t *lstr, const char *arg)
  618. {
  619. int arglen = strlen(arg);
  620. int len = lstr->len + arglen;
  621. if (len >= lstr->size) {
  622. /* realloc some more */
  623. lstr->size += arglen > 4095 ? arglen+1 : 4096;
  624. lstr->str = (char *)ldapu_realloc(lstr->str, lstr->size);
  625. if (!lstr->str) return LDAPU_ERR_OUT_OF_MEMORY;
  626. }
  627. memcpy((void *)&(lstr->str[lstr->len]), (void *)arg, arglen);
  628. lstr->len += arglen;
  629. lstr->str[lstr->len] = 0;
  630. return LDAPU_SUCCESS;
  631. }
  632. /*
  633. * ldapu_auth_userdn_groupids_recurse:
  634. * Description:
  635. * Checks if the user is member of the given comma separated list of
  636. * group names.
  637. * Arguments:
  638. * ld Pointer to LDAP (assumes connection has been
  639. * established and the client has called the
  640. * appropriate bind routine)
  641. * filter filter to use in the search
  642. * groupids some representation of group names. Example,
  643. * a comma separated names in a string, hash
  644. * table, etc. This function doesn't need to
  645. * know the name of the groups. It calls the
  646. * following function to check if one of the
  647. * groups returned by the search is in the list.
  648. * grpcmpfn group name comparison function.
  649. * base basedn (where to start the search)
  650. * recurse_cnt recursion count to detect circular groups
  651. * group_out if successful, pointer to the user's group
  652. * Return Values: (same as ldapu_find)
  653. * LDAPU_SUCCESS if user is member of one of the groups
  654. * LDAPU_FAILED if user is not a member of the group
  655. * <rv> if error, where <rv> can be passed to
  656. * ldap_err2string to get an error string.
  657. */
  658. static int ldapu_auth_userdn_groupids_recurse (LDAP *ld, const char *filter,
  659. void *groupids,
  660. LDAPU_GroupCmpFn_t grpcmpfn,
  661. const char *base,
  662. int recurse_cnt,
  663. char **group_out)
  664. {
  665. LDAPMessage *res = 0;
  666. const char *attrs[] = { "CN", 0 };
  667. int attrsonly = 0;
  668. LDAPMessage *entry;
  669. int rv;
  670. int retval;
  671. int i;
  672. int done;
  673. if (recurse_cnt >= 30)
  674. return LDAPU_ERR_CIRCULAR_GROUPS;
  675. /* Perform the ldap lookup */
  676. retval = ldapu_find(ld, base, LDAP_SCOPE_SUBTREE, filter, attrs,
  677. attrsonly, &res);
  678. if (retval != LDAPU_SUCCESS && retval != LDAPU_ERR_MULTIPLE_MATCHES ) {
  679. /* user is not a member of any group */
  680. if (res) ldap_msgfree(res);
  681. return retval;
  682. }
  683. retval = LDAPU_FAILED;
  684. done = 0;
  685. /* check if one of the matched groups is one of the given groups */
  686. for (entry = ldap_first_entry(ld, res); entry != NULL && !done;
  687. entry = ldap_next_entry(ld, entry))
  688. {
  689. struct berval **bvals;
  690. if ((bvals = ldap_get_values_len(ld, entry, "CN")) == NULL) {
  691. /* This shouldn't happen */
  692. retval = LDAPU_ERR_MISSING_ATTR_VAL;
  693. continue;
  694. }
  695. /* "CN" may have multiple values */
  696. /* Check each value of "CN" against the 'groupids' */
  697. for ( i = 0; bvals[i] != NULL; i++ ) {
  698. rv = (*grpcmpfn)(groupids, bvals[i]->bv_val, bvals[i]->bv_len);
  699. if (rv == LDAPU_SUCCESS) {
  700. char *group = (char *)ldapu_malloc(bvals[i]->bv_len+1);
  701. if (!group) {
  702. retval = LDAPU_ERR_OUT_OF_MEMORY;
  703. }
  704. else {
  705. strncpy(group, bvals[i]->bv_val, bvals[i]->bv_len);
  706. group[bvals[i]->bv_len] = 0;
  707. *group_out = group;
  708. retval = LDAPU_SUCCESS;
  709. }
  710. done = 1; /* exit from the outer loop too */
  711. break;
  712. }
  713. }
  714. ldap_value_free_len(bvals);
  715. }
  716. if (retval == LDAPU_FAILED) {
  717. /* None of the matched groups is in 'groupids' */
  718. /* Perform the nested group membership check */
  719. LDAPUStr_t *filter1;
  720. LDAPUStr_t *filter2;
  721. char *rfilter = 0;
  722. int rlen;
  723. /* Finally we need a filter which looks like:
  724. (| (& (objectclass=groupofuniquenames)
  725. (| (uniquemember=<grp1dn>)(uniquemember=<grp2dn>) ...))
  726. (& (objectclass=groupofnames)
  727. (| (member=<grp1dn>)(member=<grp2dn>) ...)))
  728. Construct 2 sub-filters first as follows:
  729. (uniquemember=<grp1dn>)(uniquemember=<grp2dn>)... AND
  730. (member=<grp1dn>)(member=<grp2dn>)...
  731. Then insert them in the main filter.
  732. */
  733. filter1 = ldapu_str_alloc(1024);
  734. filter2 = ldapu_str_alloc(1024);
  735. if (!filter1 || !filter2) return LDAPU_ERR_OUT_OF_MEMORY;
  736. rv = LDAPU_SUCCESS;
  737. for (entry = ldap_first_entry(ld, res); entry != NULL;
  738. entry = ldap_next_entry(ld, entry))
  739. {
  740. char *dn = ldap_get_dn(ld, entry);
  741. if (((rv = ldapu_str_append(filter1, "(uniquemember="))
  742. != LDAPU_SUCCESS) ||
  743. ((rv = ldapu_str_append(filter1, dn)) != LDAPU_SUCCESS) ||
  744. ((rv = ldapu_str_append(filter1, ")")) != LDAPU_SUCCESS) ||
  745. ((rv = ldapu_str_append(filter2, "(member="))
  746. != LDAPU_SUCCESS) ||
  747. ((rv = ldapu_str_append(filter2, dn)) != LDAPU_SUCCESS) ||
  748. ((rv = ldapu_str_append(filter2, ")")) != LDAPU_SUCCESS))
  749. {
  750. ldap_memfree(dn);
  751. break;
  752. }
  753. ldap_memfree(dn);
  754. }
  755. if (rv != LDAPU_SUCCESS) {
  756. /* something went wrong in appending to filter1 or filter2 */
  757. ldapu_str_free(filter1);
  758. ldapu_str_free(filter2);
  759. retval = rv;
  760. }
  761. else {
  762. /* Insert the 2 filters in the main filter */
  763. rlen = filter1->len + filter2->len +
  764. strlen("(| (& (objectclass=groupofuniquenames)"
  765. "(| ))"
  766. "(& (objectclass=groupofnames)"
  767. "(| )))") + 1;
  768. rfilter = (char *)ldapu_malloc(rlen);
  769. if (!rfilter) return LDAPU_ERR_OUT_OF_MEMORY;
  770. sprintf(rfilter,
  771. "(| (& (objectclass=groupofuniquenames)"
  772. "(| %s))"
  773. "(& (objectclass=groupofnames)"
  774. "(| %s)))",
  775. filter1->str, filter2->str);
  776. ldapu_str_free(filter1);
  777. ldapu_str_free(filter2);
  778. retval = ldapu_auth_userdn_groupids_recurse(ld, rfilter, groupids,
  779. grpcmpfn, base,
  780. ++recurse_cnt,
  781. group_out);
  782. ldapu_free(rfilter);
  783. }
  784. }
  785. if (res) ldap_msgfree(res);
  786. return retval;
  787. }
  788. /*
  789. * ldapu_auth_userdn_groupids:
  790. * Description:
  791. * Checks if the user is member of the given comma separated list of
  792. * group names.
  793. * Arguments:
  794. * ld Pointer to LDAP (assumes connection has been
  795. * established and the client has called the
  796. * appropriate bind routine)
  797. * userdn User's full DN
  798. * groupids some representation of group names. Example,
  799. * a comma separated names in a string, hash
  800. * table, etc. This function doesn't need to
  801. * know the name of the groups. It calls the
  802. * following function to check if one of the
  803. * groups returned by the search is in the list.
  804. * grpcmpfn group name comparison function.
  805. * base basedn (where to start the search)
  806. * group_out if successful, pointer to the user's group
  807. * Return Values: (same as ldapu_find)
  808. * LDAPU_SUCCESS if user is member of one of the groups
  809. * LDAPU_FAILED if user is not a member of the group
  810. * <rv> if error, where <rv> can be passed to
  811. * ldap_err2string to get an error string.
  812. */
  813. int ldapu_auth_userdn_groupids (LDAP *ld, const char *userdn,
  814. void *groupids,
  815. LDAPU_GroupCmpFn_t grpcmpfn,
  816. const char *base,
  817. char **group_out)
  818. {
  819. char *filter;
  820. int len;
  821. int rv;
  822. *group_out = 0;
  823. /* allocate a big enough filter */
  824. /* The filter looks like:
  825. (| (& (objectclass=groupofuniquenames)(uniquemember=<userdn>))
  826. (& (objectclass=groupofnames)(member=<userdn>)))
  827. */
  828. len = 2 * strlen(userdn) + 1 +
  829. strlen("(| (& (objectclass=groupofuniquenames)(uniquemember=))"
  830. "(& (objectclass=groupofnames)(member=)))");
  831. filter = (char *)ldapu_malloc(len);
  832. if (!filter) return LDAPU_ERR_OUT_OF_MEMORY;
  833. sprintf(filter, "(| (& (objectclass=groupofuniquenames)(uniquemember=%s))"
  834. "(& (objectclass=groupofnames)(member=%s)))",
  835. userdn, userdn);
  836. rv = ldapu_auth_userdn_groupids_recurse(ld, filter, groupids,
  837. grpcmpfn, base,
  838. 0, group_out);
  839. ldapu_free(filter);
  840. return rv;
  841. }
  842. /*
  843. * ldapu_auth_userdn_attrfilter:
  844. * Description:
  845. * Checks if the user's entry has the given attributes
  846. * Arguments:
  847. * ld Pointer to LDAP (assumes connection has been
  848. * established and the client has called the
  849. * appropriate bind routine)
  850. * userdn User's full DN
  851. * attrfilter attribute filter
  852. * Return Values: (same as ldapu_find)
  853. * LDAPU_SUCCESS if user is member of the group
  854. * LDAPU_FAILED if user is not a member of the group
  855. * <rv> if error, where <rv> can be passed to
  856. * ldap_err2string to get an error string.
  857. */
  858. int ldapu_auth_userdn_attrfilter (LDAP *ld, const char *userdn,
  859. const char *attrfilter)
  860. {
  861. const char *base = userdn;
  862. int scope = LDAP_SCOPE_BASE;
  863. const char *filter = attrfilter;
  864. const char **attrs = default_search_attrs;
  865. int attrsonly = default_search_attrsonly;
  866. LDAPMessage *res = 0;
  867. int retval;
  868. retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, &res);
  869. if (res) ldapu_msgfree(ld, res);
  870. return retval;
  871. }
  872. /*
  873. * ldapu_auth_uid_attrfilter:
  874. * Description:
  875. * Checks if the user's entry has the given attributes. First maps
  876. the uid to userdn.
  877. * Arguments:
  878. * ld Pointer to LDAP (assumes connection has been
  879. * established and the client has called the
  880. * appropriate bind routine)
  881. * uid User's name
  882. * attrfilter attribute filter
  883. * base basedn (where to start the search)
  884. * Return Values: (same as ldapu_find)
  885. * LDAPU_SUCCESS if user is member of the group
  886. * LDAPU_FAILED if user is not a member of the group
  887. * <rv> if error, where <rv> can be passed to
  888. * ldap_err2string to get an error string.
  889. */
  890. int ldapu_auth_uid_attrfilter (LDAP *ld, const char *uid, const char *attrfilter,
  891. const char *base)
  892. {
  893. int scope = LDAP_SCOPE_SUBTREE;
  894. char filter[ BUFSIZ ];
  895. const char **attrs = default_search_attrs;
  896. int attrsonly = default_search_attrsonly;
  897. LDAPMessage *res = 0;
  898. int retval;
  899. /* setup filter as (& (uid=<uid>) (attrfilter)) */
  900. if (*attrfilter == '(')
  901. PR_snprintf(filter, sizeof(filter), "(& (uid=%s) %s)", uid, attrfilter);
  902. else
  903. PR_snprintf(filter, sizeof(filter), "(& (uid=%s) (%s))", uid, attrfilter);
  904. retval = ldapu_find(ld, base, scope, filter, attrs, attrsonly, &res);
  905. if (res) ldapu_msgfree(ld, res);
  906. return retval;
  907. }
  908. /*
  909. * ldapu_auth_userdn_password:
  910. * Description:
  911. * Checks the user's password against LDAP by binding using the
  912. * userdn and the password.
  913. * Arguments:
  914. * ld Pointer to LDAP (assumes connection has been
  915. * established and the client has called the
  916. * appropriate bind routine)
  917. * userdn User's full DN
  918. * password User's password (clear text)
  919. * Return Values: (same as ldapu_find)
  920. * LDAPU_SUCCESS if user credentials are valid
  921. * <rv> if error, where <rv> can be passed to
  922. * ldap_err2string to get an error string.
  923. */
  924. int ldapu_auth_userdn_password (LDAP *ld, const char *userdn, const char *password)
  925. {
  926. int retval;
  927. DBG_PRINT2("\tuserdn:\t\"%s\"\n", userdn);
  928. DBG_PRINT2("\tpassword:\t\"%s\"\n", password);
  929. retval = ldap_simple_bind_s(ld, userdn, password);
  930. if (retval != LDAP_SUCCESS)
  931. {
  932. DBG_PRINT2("ldap_simple_bind_s: %s\n", ldap_err2string(retval));
  933. return(retval);
  934. }
  935. return LDAPU_SUCCESS;
  936. }
  937. /*
  938. * ldapu_auth_uid_password:
  939. * Description:
  940. * First converts the uid to userdn and calls
  941. * ldapu_auth_userdn_password.
  942. * Arguments:
  943. * ld Pointer to LDAP (assumes connection has been
  944. * established and the client has called the
  945. * appropriate bind routine)
  946. * uid User's name
  947. * password User's password (clear text)
  948. * Return Values: (same as ldapu_find)
  949. * LDAPU_SUCCESS if user credentials are valid
  950. * <rv> if error, where <rv> can be passed to
  951. * ldap_err2string to get an error string.
  952. */
  953. int ldapu_auth_uid_password (LDAP *ld, const char *uid,
  954. const char *password, const char *base)
  955. {
  956. int retval;
  957. char *dn;
  958. /* First find userdn for the given uid and
  959. then call ldapu_auth_userdn_password */
  960. retval = ldapu_find_userdn(ld, uid, base, &dn);
  961. if (retval == LDAPU_SUCCESS) {
  962. retval = ldapu_auth_userdn_password(ld, dn, password);
  963. ldapu_memfree(ld, dn);
  964. }
  965. return retval;
  966. }
  967. /* ldapu_string_set --
  968. * This function is not tested yet for its usefulness. This is to be used to
  969. * customize the strings used in the LDAP searches performed through
  970. * 'ldaputil'. This could also be extended to setting customized error
  971. * messages (and even i18n equivalent of error messages).
  972. */
  973. NSAPI_PUBLIC int ldapu_string_set (const int type, const char *filter)
  974. {
  975. if (!filter || !*filter) return LDAPU_ERR_INVALID_STRING;
  976. if (type < 0 || type >= LDAPU_STR_MAX_INDEX)
  977. return LDAPU_ERR_INVALID_STRING_INDEX;
  978. ldapu_strings[type] = strdup(filter);
  979. if (!ldapu_strings[type]) return LDAPU_ERR_OUT_OF_MEMORY;
  980. return LDAPU_SUCCESS;
  981. }
  982. NSAPI_PUBLIC const char *ldapu_string_get (const int type)
  983. {
  984. if (type < 0 || type >= LDAPU_STR_MAX_INDEX)
  985. return 0;
  986. return ldapu_strings[type];
  987. }