auth.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606
  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. #include <stdio.h>
  39. #include <stdlib.h>
  40. #include <ctype.h>
  41. #include <string.h>
  42. #include <prinit.h> // for PR_Init
  43. #include <prpriv.h> // for PR_Exit
  44. #include <ldaputil/certmap.h>
  45. #include <ldaputil/init.h>
  46. #include <ldaputil/ldapdb.h>
  47. #include <ldaputil/ldapauth.h>
  48. #include <ldaputil/dbconf.h>
  49. #include <ldaputil/ldaputil.h>
  50. #include <ldap.h>
  51. static const char* dllname = "plugin.so";
  52. char *global_issuer_dn = "o=Fedora Project, c=US";
  53. #define NSPR_INIT(Program) (PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 8))
  54. static int ldapu_certinfo_save_test (const char *fname, const char *old_fname)
  55. {
  56. int rv;
  57. /* Read the original certmap config file first */
  58. rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL);
  59. if (rv != LDAPU_SUCCESS) {
  60. fprintf(stderr, "ldapu_certinfo_save_test failed. Reason: %s\n",
  61. ldapu_err2string(rv));
  62. return rv;
  63. }
  64. rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp");
  65. if (rv != LDAPU_SUCCESS) {
  66. fprintf(stderr, "ldapu_certinfo_save_test failed. Reason: %s\n",
  67. ldapu_err2string(rv));
  68. }
  69. return rv;
  70. }
  71. static int ldapu_certinfo_delete_test (const char *fname, const char *old_fname)
  72. {
  73. int rv;
  74. /* Read the original certmap config file first */
  75. rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL);
  76. if (rv != LDAPU_SUCCESS) {
  77. fprintf(stderr, "ldapu_certinfo_delete_test failed. Reason: %s\n",
  78. ldapu_err2string(rv));
  79. return rv;
  80. }
  81. /* rv = ldapu_certinfo_delete("o=Ace Industry, c=US"); */
  82. rv = ldapu_certinfo_delete("o=Fedora Project, c=US");
  83. if (rv != LDAPU_SUCCESS) {
  84. fprintf(stderr, "ldapu_certinfo_delete failed. Reason: %s\n",
  85. ldapu_err2string(rv));
  86. return rv;
  87. }
  88. rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp");
  89. if (rv != LDAPU_SUCCESS) {
  90. fprintf(stderr, "ldapu_certinfo_delete_test failed. Reason: %s\n",
  91. ldapu_err2string(rv));
  92. }
  93. return rv;
  94. }
  95. static int ldapu_certinfo_new_test (const char *fname, const char *old_fname)
  96. {
  97. int rv;
  98. LDAPUPropValList_t *propval_list;
  99. LDAPUPropVal_t *propval;
  100. /* Read the original certmap config file first */
  101. rv = ldaputil_init(old_fname, dllname, NULL, NULL, NULL);
  102. if (rv != LDAPU_SUCCESS) {
  103. fprintf(stderr, "ldapu_certinfo_new_test failed. Reason: %s\n",
  104. ldapu_err2string(rv));
  105. return rv;
  106. }
  107. /* Setup propval_list */
  108. rv = ldapu_list_alloc(&propval_list);
  109. if (rv != LDAPU_SUCCESS) return rv;
  110. rv = ldapu_propval_alloc("prop1", "val1", &propval);
  111. if (rv != LDAPU_SUCCESS) return rv;
  112. rv = ldapu_list_add_info(propval_list, propval);
  113. if (rv != LDAPU_SUCCESS) return rv;
  114. rv = ldapu_propval_alloc("prop2", "val2", &propval);
  115. if (rv != LDAPU_SUCCESS) return rv;
  116. rv = ldapu_list_add_info(propval_list, propval);
  117. if (rv != LDAPU_SUCCESS) return rv;
  118. rv = ldapu_propval_alloc("prop3", 0, &propval);
  119. if (rv != LDAPU_SUCCESS) return rv;
  120. rv = ldapu_list_add_info(propval_list, propval);
  121. if (rv != LDAPU_SUCCESS) return rv;
  122. rv = ldapu_certinfo_modify("newmap", "o=Mcom Communications, c=US",
  123. propval_list);
  124. ldapu_propval_list_free(propval_list);
  125. if (rv != LDAPU_SUCCESS) {
  126. fprintf(stderr, "ldapu_certinfo_delete failed. Reason: %s\n",
  127. ldapu_err2string(rv));
  128. return rv;
  129. }
  130. rv = ldapu_certinfo_save(fname, old_fname, "certmap.tmp");
  131. if (rv != LDAPU_SUCCESS) {
  132. fprintf(stderr, "ldapu_certinfo_new_test failed. Reason: %s\n",
  133. ldapu_err2string(rv));
  134. }
  135. return rv;
  136. }
  137. static int get_dbnames_test (const char *mapfile)
  138. {
  139. char **names;
  140. int cnt;
  141. int rv;
  142. int i;
  143. rv = dbconf_get_dbnames(mapfile, &names, &cnt);
  144. if (rv != LDAPU_SUCCESS) {
  145. fprintf(stderr, "get_dbnames_test failed. Reason: %s\n",
  146. ldapu_err2string(rv));
  147. }
  148. else {
  149. for(i = 0; i < cnt; i++) {
  150. fprintf(stderr, "\tdbname[%d] = \"%s\"\n",
  151. i, names[i]);
  152. }
  153. }
  154. dbconf_free_dbnames(names);
  155. return rv;
  156. }
  157. static int case_ignore_strcmp (const char *s1, const char *s2)
  158. {
  159. int ls1, ls2; /* tolower values of chars in s1 & s2 resp. */
  160. if (!s1) return !s2 ? 0 : 0-tolower(*s2);
  161. else if (!s2) return tolower(*s1);
  162. while(*s1 && *s2 && (ls1 = tolower(*s1)) == (ls2 = tolower(*s2))) { s1++; s2++; }
  163. if (!*s1)
  164. return *s2 ? 0-tolower(*s2) : 0;
  165. else if (!*s2)
  166. return tolower(*s1);
  167. else
  168. return ls1 - ls2;
  169. }
  170. #define STRCASECMP3(s1, s2, rv) \
  171. { \
  172. int i = case_ignore_strcmp(s1, s2); \
  173. fprintf(stderr, "strcasecmp(\"%s\", \"%s\")\t=\t%d\t%s\tExpected: %d\n", \
  174. s1 ? s1 : "<NULL>", s2 ? s2 : "<NULL>", \
  175. i, i == rv ? "SUCCESS" : "FAILED", rv); \
  176. }
  177. #ifndef XP_WIN32
  178. #define STRCASECMP(s1, s2) STRCASECMP3(s1, s2, strcasecmp(s1, s2))
  179. #else
  180. #define STRCASECMP(s1, s2) STRCASECMP3(s1, s2, case_ignore_strcmp(s1, s2))
  181. #endif
  182. static void strcasecmp_test ()
  183. {
  184. STRCASECMP3(0, "aBcD", 0-tolower('a'));
  185. STRCASECMP3(0, 0, 0);
  186. STRCASECMP3("aBcD", 0, tolower('a'));
  187. STRCASECMP("AbCd", "aBcD");
  188. STRCASECMP("AbCd", "abcd");
  189. STRCASECMP("ABCD", "ABCD");
  190. STRCASECMP("abcd", "abcd");
  191. STRCASECMP("AbCd", "aBcD3");
  192. STRCASECMP("AbCd", "abcd3");
  193. STRCASECMP("ABCD", "ABCD3");
  194. STRCASECMP("abcd", "abcd3");
  195. STRCASECMP("AbCd1", "aBcD");
  196. STRCASECMP("AbCd2", "abcd");
  197. STRCASECMP("ABCDX", "ABCD");
  198. STRCASECMP("abcdY", "abcd");
  199. STRCASECMP("AbCd5", "aBcD1");
  200. STRCASECMP("AbCd5", "abcd1");
  201. STRCASECMP("ABCD5", "ABCD1");
  202. STRCASECMP("abcd5", "abcd1");
  203. STRCASECMP("AbCd2", "aBcDp");
  204. STRCASECMP("AbCd2", "abcdQ");
  205. STRCASECMP("ABCD2", "ABCDr");
  206. STRCASECMP("abcd2", "abcdS");
  207. }
  208. static int certmap_tests (const char *config_file) { return 0; }
  209. static int read_config_test (const char *config_file, const char *dbname,
  210. const char *url,
  211. const char *binddn, const char *bindpw)
  212. {
  213. int rv;
  214. DBConfDBInfo_t *db_info;
  215. char *dn;
  216. char *pw;
  217. rv = dbconf_read_default_dbinfo(config_file, &db_info);
  218. if (rv != LDAPU_SUCCESS) {
  219. fprintf(stderr, "config_test failed: %s\n",
  220. ldapu_err2string(rv));
  221. return LDAPU_FAILED;
  222. }
  223. if (strcmp(db_info->dbname, dbname) ||
  224. strcmp(db_info->url, url)) {
  225. fprintf(stderr, "config_test failed: %s\n",
  226. "first line in config file is wrong");
  227. return LDAPU_FAILED;
  228. }
  229. if ((ldapu_dbinfo_attrval(db_info, "binddn", &dn) != LDAPU_SUCCESS) ||
  230. (ldapu_dbinfo_attrval(db_info, "bindpw", &pw) != LDAPU_SUCCESS))
  231. {
  232. fprintf(stderr, "config_test failed: %s\n",
  233. "properties are missing");
  234. return LDAPU_FAILED;
  235. }
  236. if (strcmp(dn, binddn) ||
  237. strcmp(pw, bindpw)) {
  238. fprintf(stderr, "config_test failed: %s\n",
  239. "property values are wrong");
  240. return LDAPU_FAILED;
  241. }
  242. fprintf(stderr, "binddn from config file: \"%s\"\n", dn);
  243. fprintf(stderr, "bindpw from config file: \"%s\"\n", pw);
  244. /* cleanup */
  245. dbconf_free_dbinfo(db_info);
  246. free(dn);
  247. free(pw);
  248. return LDAPU_SUCCESS;
  249. }
  250. static int config_test (const char *binddn, const char *bindpw)
  251. {
  252. char *config_file = "config_out.conf";
  253. FILE *fp = fopen(config_file, "w");
  254. const char *dbname = "default";
  255. const char *url = "file:/foobar/path";
  256. int rv;
  257. if (!fp) return LDAPU_FAILED;
  258. dbconf_output_db_directive(fp, dbname, url);
  259. dbconf_output_propval(fp, dbname, "binddn", binddn, 0);
  260. dbconf_output_propval(fp, dbname, "bindpw", bindpw, 1);
  261. fclose(fp);
  262. fprintf(stderr, "Config file written: %s\n", config_file);
  263. rv = read_config_test(config_file, dbname, url, binddn, bindpw);
  264. return rv;
  265. }
  266. static int
  267. compare_groupid(const void *arg, const char *group, const int len)
  268. {
  269. auto const char* groupid = (const char*)arg;
  270. auto int err = LDAPU_FAILED;
  271. if (len == strlen (groupid) && !strncasecmp (groupid, group, len)) {
  272. err = LDAPU_SUCCESS;
  273. }
  274. return err;
  275. }
  276. static int
  277. compare_group(LDAP* directory, LDAPMessage* entry, void* set)
  278. {
  279. auto int err = LDAPU_FAILED;
  280. auto char** vals = ldap_get_values (directory, entry, "CN");
  281. if (vals) {
  282. auto char** val;
  283. for (val = vals; *val; ++val) {
  284. if (!strcasecmp (*val, (char*)set)) {
  285. err = LDAPU_SUCCESS;
  286. break;
  287. }
  288. }
  289. ldap_value_free (vals);
  290. }
  291. return err;
  292. }
  293. int perform_test (int argc, char *argv[])
  294. {
  295. int test_type;
  296. int retval = LDAPU_SUCCESS;
  297. DBConfDBInfo_t *db_info;
  298. LDAPDatabase_t *ldb;
  299. LDAP *ld;
  300. char *dbmap_file = "dblist.conf";
  301. char *binddn = 0;
  302. char *bindpw = 0;
  303. char *basedn;
  304. int retry = 1;
  305. int rv;
  306. fprintf(stderr, "\nStart of test: ./auth %s \"%s\" \"%s\"\n",
  307. argv[1], argv[2], argv[3]);
  308. rv = dbconf_read_default_dbinfo(dbmap_file, &db_info);
  309. if (rv != LDAPU_SUCCESS) {
  310. fprintf(stderr, "Error reading dbmap file \"%s\". Reason: %s\n",
  311. dbmap_file, ldapu_err2string(rv));
  312. return rv;
  313. }
  314. ldapu_dbinfo_attrval (db_info, LDAPU_ATTR_BINDDN, &binddn);
  315. ldapu_dbinfo_attrval (db_info, LDAPU_ATTR_BINDPW, &bindpw);
  316. rv = ldapu_url_parse (db_info->url, binddn, bindpw, &ldb);
  317. free(binddn);
  318. free(bindpw);
  319. if (rv != LDAPU_SUCCESS) {
  320. fprintf(stderr, "Error parsing ldap url \"%s\". Reason: %s\n",
  321. db_info->url, ldapu_err2string(rv));
  322. return rv;
  323. }
  324. basedn = ldb->basedn;
  325. test_type = atoi(argv[1]);
  326. retry = 1;
  327. while(retry) {
  328. retry = 0;
  329. rv = ldapu_ldap_init_and_bind (ldb);
  330. if (rv != LDAPU_SUCCESS) {
  331. fprintf(stderr, "Error initializing connection to LDAP. Reason: %s\n",
  332. ldapu_err2string(rv));
  333. return rv;
  334. }
  335. ld = ldb->ld;
  336. switch(test_type) {
  337. case 1:
  338. fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupdn:\t\"%s\"\n",
  339. argv[2], argv[3]);
  340. retval = ldapu_auth_userdn_groupdn(ld, argv[2], argv[3], basedn);
  341. break;
  342. case 2:
  343. fprintf(stderr, "\nuid:\t\t\"%s\"\ngroupdn:\t\"%s\"\n", argv[2], argv[3]);
  344. retval = ldapu_auth_uid_groupdn(ld, argv[2], argv[3], basedn);
  345. break;
  346. case 3:
  347. fprintf(stderr, "\nuid:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]);
  348. retval = ldapu_auth_uid_groupid(ld, argv[2], argv[3], basedn);
  349. break;
  350. case 4:
  351. fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]);
  352. retval = ldapu_auth_userdn_groupid(ld, argv[2], argv[3], basedn);
  353. break;
  354. case 5:
  355. fprintf(stderr, "\nuserdn:\t\t\"%s\"\nattrFilter:\t\"%s\"\n", argv[2], argv[3]);
  356. retval = ldapu_auth_userdn_attrfilter(ld, argv[2], argv[3]);
  357. break;
  358. case 6:
  359. fprintf(stderr, "\nuid:\t\t\"%s\"\nattrFilter:\t\"%s\"\n", argv[2], argv[3]);
  360. retval = ldapu_auth_uid_attrfilter(ld, argv[2], argv[3], basedn);
  361. break;
  362. case 7:
  363. fprintf(stderr, "\nuserdn:\t\t\"%s\"\npassword:\t\"%s\"\n", argv[2], argv[3]);
  364. retval = ldapu_auth_userdn_password(ld, argv[2], argv[3]);
  365. break;
  366. case 8:
  367. fprintf(stderr, "\nuid:\t\t\"%s\"\npassword:\t\"%s\"\n", argv[2], argv[3]);
  368. retval = ldapu_auth_uid_password(ld, argv[2], argv[3], basedn);
  369. break;
  370. case 9: {
  371. /* plugin test */
  372. LDAPMessage *entry = 0;
  373. LDAPMessage *res = 0;
  374. fprintf(stderr, "Cert Map issuer DN: \"%s\"\n", argv[2]);
  375. fprintf(stderr, "Cert Map subject DN: \"%s\"\n", argv[3]);
  376. retval = ldaputil_init("certmap.conf", dllname, NULL, NULL, NULL);
  377. if (retval != LDAPU_SUCCESS) {
  378. fprintf(stderr, "Cert Map info test failed. Reason: %s\n",
  379. ldapu_err2string(retval));
  380. break;
  381. }
  382. if (*(argv[2]))
  383. global_issuer_dn = argv[2];
  384. else
  385. global_issuer_dn = 0;
  386. retval = ldapu_cert_to_ldap_entry(argv[3], ld, ldb->basedn, &res);
  387. if (retval == LDAPU_SUCCESS) {
  388. char *dn;
  389. entry = ldap_first_entry(ld, res);
  390. dn = ldap_get_dn(ld, entry);
  391. fprintf(stderr, "Matched entry to cert: \"%s\"\n", dn);
  392. ldap_memfree(dn);
  393. }
  394. else if (retval == LDAPU_FAILED) {
  395. /* Not an error but couldn't map the cert */
  396. }
  397. else {
  398. fprintf(stderr, "Cert Map info test failed. Reason: %s\n",
  399. ldapu_err2string(retval));
  400. break;
  401. }
  402. /* TEMPORARY -- when & how to free the entry */
  403. if (res) ldap_msgfree(res);
  404. break;
  405. } /* case 9 */
  406. case 10:
  407. if ((retval = config_test(argv[2], argv[3])) == LDAPU_SUCCESS) {
  408. fprintf(stderr, "Config file test succeeded\n");
  409. }
  410. else {
  411. fprintf(stderr, "Config file test failed\n");
  412. }
  413. break;
  414. case 11:
  415. retval = get_dbnames_test(argv[2]);
  416. break;
  417. case 12:
  418. retval = ldapu_certinfo_save_test(argv[2], argv[3]);
  419. break;
  420. case 13:
  421. retval = ldapu_certinfo_delete_test(argv[2], argv[3]);
  422. break;
  423. case 14:
  424. retval = ldapu_certinfo_new_test(argv[2], argv[3]);
  425. break;
  426. case 15:
  427. fprintf(stderr, "\nuserdn:\t\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]);
  428. {
  429. auto LDAPU_DNList_t* userDNs = ldapu_DNList_alloc();
  430. ldapu_DNList_add(userDNs, argv[2]);
  431. retval = ldapu_auth_usercert_groups(ld, basedn, userDNs, NULL,
  432. argv[3], compare_group, 30, NULL);
  433. ldapu_DNList_free(userDNs);
  434. }
  435. break;
  436. case 16:
  437. fprintf(stderr, "\nuserCert:\t\"%s\"\ngroupid:\t\"%s\"\n", argv[2], argv[3]);
  438. retval = ldapu_auth_usercert_groupids(ld, NULL/*userDN*/, argv[2], argv[3],
  439. compare_groupid, basedn, NULL/*group_out*/);
  440. break;
  441. } /* switch */
  442. if (retval == LDAP_SERVER_DOWN) {
  443. /* retry */
  444. retry = 1;
  445. ldb->ld = 0;
  446. }
  447. else if (retval == LDAPU_SUCCESS) {
  448. fprintf(stderr, "Authentication succeeded.\n");
  449. }
  450. else {
  451. fprintf(stderr, "Authentication failed.\n");
  452. }
  453. }
  454. /* cleanup */
  455. // ldapu_free_LDAPDatabase_t(ldb);
  456. // dbconf_free_dbinfo(db_info);
  457. // ldaputil_exit();
  458. return retval;
  459. }
  460. int main (int argc, char *argv[])
  461. {
  462. int rv;
  463. NSPR_INIT("auth");
  464. if (argc != 4) {
  465. fprintf(stderr, "argc = %d\n", argc);
  466. fprintf(stderr, "usage: %s test_type user_dn group_dn\n", argv[0]);
  467. fprintf(stderr, "\t%s 1 <userdn> <groupdn>\n", argv[0]);
  468. fprintf(stderr, "\t%s 2 <uid> <groupdn>\n", argv[0]);
  469. fprintf(stderr, "\t%s 3 <uid> <groupid>\n", argv[0]);
  470. fprintf(stderr, "\t%s 4 <userdn> <groupid>\n", argv[0]);
  471. fprintf(stderr, "\t%s 5 <userdn> <attrFilter>\n", argv[0]);
  472. fprintf(stderr, "\t%s 6 <uid> <attrFilter>\n", argv[0]);
  473. fprintf(stderr, "\t%s 7 <userdn> <password>\n", argv[0]);
  474. fprintf(stderr, "\t%s 8 <uid> <password>\n", argv[0]);
  475. fprintf(stderr, "\t%s 9 <certmap.conf> <subjectDN>\n", argv[0]);
  476. fprintf(stderr, "\t%s 10 <binddn> <bindpw>\n", argv[0]);
  477. fprintf(stderr, "\t%s 11 <dbmap> <ignore>\n", argv[0]);
  478. fprintf(stderr, "\t%s 12 <newconfig> <oldconfig> ... to test save\n", argv[0]);
  479. fprintf(stderr, "\t%s 13 <newconfig> <oldconfig> ... to test delete\n", argv[0]);
  480. fprintf(stderr, "\t%s 14 <newconfig> <oldconfig> ... to test add\n", argv[0]);
  481. fprintf(stderr, "\t%s 15 <userdn> <groupid>\n", argv[0]);
  482. fprintf(stderr, "\t%s 16 <userCertDescription> <groupid>\n", argv[0]);
  483. exit(LDAP_PARAM_ERROR);
  484. }
  485. rv = perform_test(argc, argv);
  486. /* PR_Exit(); */
  487. return rv;
  488. }