client.c 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317
  1. /* SASL client API implementation
  2. * Rob Siemborski
  3. * Tim Martin
  4. */
  5. /*
  6. * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved.
  7. *
  8. * Redistribution and use in source and binary forms, with or without
  9. * modification, are permitted provided that the following conditions
  10. * are met:
  11. *
  12. * 1. Redistributions of source code must retain the above copyright
  13. * notice, this list of conditions and the following disclaimer.
  14. *
  15. * 2. Redistributions in binary form must reproduce the above copyright
  16. * notice, this list of conditions and the following disclaimer in
  17. * the documentation and/or other materials provided with the
  18. * distribution.
  19. *
  20. * 3. The name "Carnegie Mellon University" must not be used to
  21. * endorse or promote products derived from this software without
  22. * prior written permission. For permission or any other legal
  23. * details, please contact
  24. * Carnegie Mellon University
  25. * Center for Technology Transfer and Enterprise Creation
  26. * 4615 Forbes Avenue
  27. * Suite 302
  28. * Pittsburgh, PA 15213
  29. * (412) 268-7393, fax: (412) 268-7395
  30. * innovation@andrew.cmu.edu
  31. *
  32. * 4. Redistributions of any form whatsoever must retain the following
  33. * acknowledgment:
  34. * "This product includes software developed by Computing Services
  35. * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
  36. *
  37. * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
  38. * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  39. * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
  40. * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  41. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  42. * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  43. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  44. */
  45. #include <config.h>
  46. #include <stdio.h>
  47. #include <stdlib.h>
  48. #include <limits.h>
  49. #include <ctype.h>
  50. #include <string.h>
  51. #ifdef HAVE_UNISTD_H
  52. #include <unistd.h>
  53. #endif
  54. /* SASL Headers */
  55. #include "sasl.h"
  56. #include "saslplug.h"
  57. #include "saslutil.h"
  58. #include "saslint.h"
  59. static cmech_list_t *cmechlist; /* global var which holds the list */
  60. static sasl_global_callbacks_t global_callbacks_client;
  61. static int _sasl_client_active = 0;
  62. static int init_mechlist()
  63. {
  64. cmechlist->utils=_sasl_alloc_utils(NULL, &global_callbacks_client);
  65. if (cmechlist->utils==NULL)
  66. return SASL_NOMEM;
  67. cmechlist->mech_list=NULL;
  68. cmechlist->mech_length=0;
  69. return SASL_OK;
  70. }
  71. int sasl_client_done(void)
  72. {
  73. int result = SASL_CONTINUE;
  74. if (_sasl_server_cleanup_hook == NULL && _sasl_client_cleanup_hook == NULL) {
  75. return SASL_NOTINIT;
  76. }
  77. if (_sasl_client_cleanup_hook) {
  78. result = _sasl_client_cleanup_hook();
  79. if (result == SASL_OK) {
  80. _sasl_client_idle_hook = NULL;
  81. _sasl_client_cleanup_hook = NULL;
  82. } else {
  83. return result;
  84. }
  85. }
  86. if (_sasl_server_cleanup_hook || _sasl_client_cleanup_hook) {
  87. return result;
  88. }
  89. sasl_common_done();
  90. return SASL_OK;
  91. }
  92. static int client_done(void) {
  93. cmechanism_t *cm;
  94. cmechanism_t *cprevm;
  95. if (!_sasl_client_active) {
  96. return SASL_NOTINIT;
  97. } else {
  98. _sasl_client_active--;
  99. }
  100. if(_sasl_client_active) {
  101. /* Don't de-init yet! Our refcount is nonzero. */
  102. return SASL_CONTINUE;
  103. }
  104. cm = cmechlist->mech_list; /* m point to beginning of the list */
  105. while (cm != NULL) {
  106. cprevm = cm;
  107. cm = cm->next;
  108. if (cprevm->m.plug->mech_free) {
  109. cprevm->m.plug->mech_free(cprevm->m.plug->glob_context,
  110. cmechlist->utils);
  111. }
  112. sasl_FREE(cprevm->m.plugname);
  113. sasl_FREE(cprevm);
  114. }
  115. _sasl_free_utils(&cmechlist->utils);
  116. sasl_FREE(cmechlist);
  117. cmechlist = NULL;
  118. return SASL_OK;
  119. }
  120. /* This is nearly identical to the version in server.c.
  121. Keep in sync. */
  122. static int mech_compare(const sasl_client_plug_t *a,
  123. const sasl_client_plug_t *b)
  124. {
  125. unsigned sec_diff;
  126. unsigned features_diff;
  127. /* XXX the following is fairly arbitrary, but its independent
  128. of the order in which the plugins are loaded
  129. */
  130. #ifdef PREFER_MECH
  131. if (!strcasecmp(a->mech_name, PREFER_MECH)) return 1;
  132. if (!strcasecmp(b->mech_name, PREFER_MECH)) return -1;
  133. #endif
  134. sec_diff = a->security_flags ^ b->security_flags;
  135. if (sec_diff & a->security_flags & SASL_SEC_NOANONYMOUS) return 1;
  136. if (sec_diff & b->security_flags & SASL_SEC_NOANONYMOUS) return -1;
  137. if (sec_diff & a->security_flags & SASL_SEC_NOPLAINTEXT) return 1;
  138. if (sec_diff & b->security_flags & SASL_SEC_NOPLAINTEXT) return -1;
  139. if (sec_diff & a->security_flags & SASL_SEC_MUTUAL_AUTH) return 1;
  140. if (sec_diff & b->security_flags & SASL_SEC_MUTUAL_AUTH) return -1;
  141. if (sec_diff & a->security_flags & SASL_SEC_NOACTIVE) return 1;
  142. if (sec_diff & b->security_flags & SASL_SEC_NOACTIVE) return -1;
  143. if (sec_diff & a->security_flags & SASL_SEC_NODICTIONARY) return 1;
  144. if (sec_diff & b->security_flags & SASL_SEC_NODICTIONARY) return -1;
  145. if (sec_diff & a->security_flags & SASL_SEC_FORWARD_SECRECY) return 1;
  146. if (sec_diff & b->security_flags & SASL_SEC_FORWARD_SECRECY) return -1;
  147. features_diff = a->features ^ b->features;
  148. if (features_diff & a->features & SASL_FEAT_CHANNEL_BINDING) return 1;
  149. if (features_diff & b->features & SASL_FEAT_CHANNEL_BINDING) return -1;
  150. if (a->max_ssf > b->max_ssf) return 1;
  151. if (a->max_ssf < b->max_ssf) return -1;
  152. if (SASL_GET_HASH_STRENGTH(a->security_flags) > SASL_GET_HASH_STRENGTH(b->security_flags)) return 1;
  153. if (SASL_GET_HASH_STRENGTH(a->security_flags) < SASL_GET_HASH_STRENGTH(b->security_flags)) return -1;
  154. return 0;
  155. }
  156. int sasl_client_add_plugin(const char *plugname,
  157. sasl_client_plug_init_t *entry_point)
  158. {
  159. int plugcount;
  160. sasl_client_plug_t *pluglist;
  161. cmechanism_t *mech, *mp;
  162. int result;
  163. int version;
  164. int lupe;
  165. if (!plugname || !entry_point) return SASL_BADPARAM;
  166. result = entry_point(cmechlist->utils,
  167. SASL_CLIENT_PLUG_VERSION,
  168. &version,
  169. &pluglist,
  170. &plugcount);
  171. if (result != SASL_OK)
  172. {
  173. _sasl_log(NULL, SASL_LOG_WARN,
  174. "sasl_client_add_plugin(): entry_point(): failed for plugname %s: %z",
  175. plugname, result);
  176. return result;
  177. }
  178. if (version != SASL_CLIENT_PLUG_VERSION)
  179. {
  180. _sasl_log(NULL, SASL_LOG_WARN,
  181. "version conflict in sasl_client_add_plugin for %s", plugname);
  182. return SASL_BADVERS;
  183. }
  184. for (lupe=0; lupe < plugcount; lupe++, pluglist++)
  185. {
  186. mech = sasl_ALLOC(sizeof(cmechanism_t));
  187. if (!mech) return SASL_NOMEM;
  188. mech->m.plug = pluglist;
  189. if (_sasl_strdup(plugname, &mech->m.plugname, NULL) != SASL_OK) {
  190. sasl_FREE(mech);
  191. return SASL_NOMEM;
  192. }
  193. mech->m.version = version;
  194. /* sort mech_list by relative "strength" */
  195. mp = cmechlist->mech_list;
  196. if (!mp || mech_compare(pluglist, mp->m.plug) >= 0) {
  197. /* add mech to head of list */
  198. mech->next = cmechlist->mech_list;
  199. cmechlist->mech_list = mech;
  200. } else {
  201. /* find where to insert mech into list */
  202. while (mp->next &&
  203. mech_compare(pluglist, mp->next->m.plug) <= 0) mp = mp->next;
  204. mech->next = mp->next;
  205. mp->next = mech;
  206. }
  207. cmechlist->mech_length++;
  208. }
  209. return SASL_OK;
  210. }
  211. static int
  212. client_idle(sasl_conn_t *conn)
  213. {
  214. cmechanism_t *m;
  215. if (! cmechlist)
  216. return 0;
  217. for (m = cmechlist->mech_list;
  218. m;
  219. m = m->next)
  220. if (m->m.plug->idle
  221. && m->m.plug->idle(m->m.plug->glob_context,
  222. conn,
  223. conn ? ((sasl_client_conn_t *)conn)->cparams : NULL))
  224. return 1;
  225. return 0;
  226. }
  227. /* initialize the SASL client drivers
  228. * callbacks -- base callbacks for all client connections
  229. * returns:
  230. * SASL_OK -- Success
  231. * SASL_NOMEM -- Not enough memory
  232. * SASL_BADVERS -- Mechanism version mismatch
  233. * SASL_BADPARAM -- error in config file
  234. * SASL_NOMECH -- No mechanisms available
  235. * ...
  236. */
  237. int sasl_client_init(const sasl_callback_t *callbacks)
  238. {
  239. int ret;
  240. const add_plugin_list_t ep_list[] = {
  241. { "sasl_client_plug_init", (add_plugin_t *)sasl_client_add_plugin },
  242. { "sasl_canonuser_init", (add_plugin_t *)sasl_canonuser_add_plugin },
  243. { NULL, NULL }
  244. };
  245. /* lock allocation type */
  246. _sasl_allocation_locked++;
  247. if(_sasl_client_active) {
  248. /* We're already active, just increase our refcount */
  249. /* xxx do something with the callback structure? */
  250. _sasl_client_active++;
  251. return SASL_OK;
  252. }
  253. global_callbacks_client.callbacks = callbacks;
  254. global_callbacks_client.appname = NULL;
  255. cmechlist=sasl_ALLOC(sizeof(cmech_list_t));
  256. if (cmechlist==NULL) return SASL_NOMEM;
  257. /* We need to call client_done if we fail now */
  258. _sasl_client_active = 1;
  259. /* load plugins */
  260. ret=init_mechlist();
  261. if (ret!=SASL_OK) {
  262. client_done();
  263. return ret;
  264. }
  265. sasl_client_add_plugin("EXTERNAL", &external_client_plug_init);
  266. ret = _sasl_common_init(&global_callbacks_client);
  267. if (ret == SASL_OK)
  268. ret = _sasl_load_plugins(ep_list,
  269. _sasl_find_getpath_callback(callbacks),
  270. _sasl_find_verifyfile_callback(callbacks));
  271. if (ret == SASL_OK) {
  272. _sasl_client_cleanup_hook = &client_done;
  273. _sasl_client_idle_hook = &client_idle;
  274. ret = _sasl_build_mechlist();
  275. } else {
  276. client_done();
  277. }
  278. return ret;
  279. }
  280. static void client_dispose(sasl_conn_t *pconn)
  281. {
  282. sasl_client_conn_t *c_conn=(sasl_client_conn_t *) pconn;
  283. if (c_conn->mech && c_conn->mech->m.plug->mech_dispose) {
  284. c_conn->mech->m.plug->mech_dispose(pconn->context,
  285. c_conn->cparams->utils);
  286. }
  287. pconn->context = NULL;
  288. if (c_conn->clientFQDN)
  289. sasl_FREE(c_conn->clientFQDN);
  290. if (c_conn->cparams) {
  291. _sasl_free_utils(&(c_conn->cparams->utils));
  292. sasl_FREE(c_conn->cparams);
  293. }
  294. if (c_conn->mech_list != cmechlist->mech_list) {
  295. /* free connection-specific mech_list */
  296. cmechanism_t *m, *prevm;
  297. m = c_conn->mech_list; /* m point to beginning of the list */
  298. while (m) {
  299. prevm = m;
  300. m = m->next;
  301. sasl_FREE(prevm);
  302. }
  303. }
  304. _sasl_conn_dispose(pconn);
  305. }
  306. /* initialize a client exchange based on the specified mechanism
  307. * service -- registered name of the service using SASL (e.g. "imap")
  308. * serverFQDN -- the fully qualified domain name of the server
  309. * iplocalport -- client IPv4/IPv6 domain literal string with port
  310. * (if NULL, then mechanisms requiring IPaddr are disabled)
  311. * ipremoteport -- server IPv4/IPv6 domain literal string with port
  312. * (if NULL, then mechanisms requiring IPaddr are disabled)
  313. * prompt_supp -- list of client interactions supported
  314. * may also include sasl_getopt_t context & call
  315. * NULL prompt_supp = user/pass via SASL_INTERACT only
  316. * NULL proc = interaction supported via SASL_INTERACT
  317. * secflags -- security flags (see above)
  318. * in/out:
  319. * pconn -- connection negotiation structure
  320. * pointer to NULL => allocate new
  321. * non-NULL => recycle storage and go for next available mech
  322. *
  323. * Returns:
  324. * SASL_OK -- success
  325. * SASL_NOMECH -- no mechanism meets requested properties
  326. * SASL_NOMEM -- not enough memory
  327. */
  328. int sasl_client_new(const char *service,
  329. const char *serverFQDN,
  330. const char *iplocalport,
  331. const char *ipremoteport,
  332. const sasl_callback_t *prompt_supp,
  333. unsigned flags,
  334. sasl_conn_t **pconn)
  335. {
  336. int result;
  337. char name[MAXFQDNLEN];
  338. sasl_client_conn_t *conn;
  339. sasl_utils_t *utils;
  340. sasl_getopt_t *getopt;
  341. void *context;
  342. const char *mlist = NULL;
  343. int plus = 0;
  344. if (_sasl_client_active == 0) return SASL_NOTINIT;
  345. /* Remember, serverFQDN, iplocalport and ipremoteport can be NULL and be valid! */
  346. if (!pconn || !service)
  347. return SASL_BADPARAM;
  348. *pconn=sasl_ALLOC(sizeof(sasl_client_conn_t));
  349. if (*pconn==NULL) {
  350. _sasl_log(NULL, SASL_LOG_ERR,
  351. "Out of memory allocating connection context");
  352. return SASL_NOMEM;
  353. }
  354. memset(*pconn, 0, sizeof(sasl_client_conn_t));
  355. (*pconn)->destroy_conn = &client_dispose;
  356. conn = (sasl_client_conn_t *)*pconn;
  357. conn->mech = NULL;
  358. conn->cparams=sasl_ALLOC(sizeof(sasl_client_params_t));
  359. if (conn->cparams==NULL)
  360. MEMERROR(*pconn);
  361. memset(conn->cparams,0,sizeof(sasl_client_params_t));
  362. result = _sasl_conn_init(*pconn, service, flags, SASL_CONN_CLIENT,
  363. &client_idle, serverFQDN,
  364. iplocalport, ipremoteport,
  365. prompt_supp, &global_callbacks_client);
  366. if (result != SASL_OK) RETURN(*pconn, result);
  367. utils = _sasl_alloc_utils(*pconn, &global_callbacks_client);
  368. if (utils == NULL) {
  369. MEMERROR(*pconn);
  370. }
  371. utils->conn= *pconn;
  372. conn->cparams->utils = utils;
  373. if(_sasl_getcallback(*pconn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) {
  374. getopt(context, NULL, "client_mech_list", &mlist, NULL);
  375. }
  376. /* if we have a client_mech_list, create ordered list of
  377. available mechanisms for this conn */
  378. if (mlist) {
  379. const char *cp;
  380. cmechanism_t *mptr, *tail = NULL;
  381. cmechanism_t *new;
  382. while (*mlist) {
  383. /* find end of current mech name */
  384. for (cp = mlist; *cp && !isspace((int) *cp); cp++);
  385. /* search for mech name in loaded plugins */
  386. for (mptr = cmechlist->mech_list; mptr; mptr = mptr->next) {
  387. const sasl_client_plug_t *plug = mptr->m.plug;
  388. if (_sasl_is_equal_mech(mlist, plug->mech_name, (size_t) (cp - mlist), &plus)) {
  389. /* found a match */
  390. break;
  391. }
  392. }
  393. if (mptr) {
  394. new = sasl_ALLOC(sizeof(cmechanism_t));
  395. if (!new) {
  396. result = SASL_NOMEM;
  397. goto failed_client_new;
  398. }
  399. memcpy(&new->m, &mptr->m, sizeof(client_sasl_mechanism_t));
  400. new->next = NULL;
  401. if (!conn->mech_list) {
  402. conn->mech_list = new;
  403. tail = conn->mech_list;
  404. } else {
  405. if (tail)
  406. tail->next = new;
  407. tail = new;
  408. }
  409. conn->mech_length++;
  410. }
  411. /* find next mech name */
  412. mlist = cp;
  413. while (*mlist && isspace((int) *mlist)) mlist++;
  414. }
  415. } else {
  416. conn->mech_list = cmechlist->mech_list;
  417. conn->mech_length = cmechlist->mech_length;
  418. }
  419. if (conn->mech_list == NULL) {
  420. sasl_seterror(*pconn, 0, "No worthy mechs found");
  421. result = SASL_NOMECH;
  422. goto failed_client_new;
  423. }
  424. /* Setup the non-lazy parts of cparams, the rest is done in
  425. * sasl_client_start */
  426. conn->cparams->canon_user = &_sasl_canon_user_lookup;
  427. conn->cparams->flags = flags;
  428. conn->cparams->prompt_supp = (*pconn)->callbacks;
  429. /* get the clientFQDN (serverFQDN was set in _sasl_conn_init) */
  430. memset(name, 0, sizeof(name));
  431. if (get_fqhostname (name, MAXFQDNLEN, 0) != 0) {
  432. return (SASL_FAIL);
  433. }
  434. result = _sasl_strdup(name, &conn->clientFQDN, NULL);
  435. if (result == SASL_OK) return SASL_OK;
  436. failed_client_new:
  437. /* result isn't SASL_OK */
  438. _sasl_conn_dispose(*pconn);
  439. sasl_FREE(*pconn);
  440. *pconn = NULL;
  441. _sasl_log(NULL, SASL_LOG_ERR, "Out of memory in sasl_client_new");
  442. return result;
  443. }
  444. static int have_prompts(sasl_conn_t *conn,
  445. const sasl_client_plug_t *mech)
  446. {
  447. static const unsigned long default_prompts[] = {
  448. SASL_CB_AUTHNAME,
  449. SASL_CB_PASS,
  450. SASL_CB_LIST_END
  451. };
  452. const unsigned long *prompt;
  453. sasl_callback_ft pproc;
  454. void *pcontext;
  455. int result;
  456. for (prompt = (mech->required_prompts
  457. ? mech->required_prompts :
  458. default_prompts);
  459. *prompt != SASL_CB_LIST_END;
  460. prompt++) {
  461. result = _sasl_getcallback(conn, *prompt, &pproc, &pcontext);
  462. if (result != SASL_OK && result != SASL_INTERACT)
  463. return 0; /* we don't have this required prompt */
  464. }
  465. return 1; /* we have all the prompts */
  466. }
  467. static int
  468. _mech_plus_p(const char *mech, size_t len)
  469. {
  470. return (len > 5 && strncasecmp(&mech[len - 5], "-PLUS", 5) == 0);
  471. }
  472. /*
  473. * Order PLUS mechanisms first. Returns NUL separated list of
  474. * *count items.
  475. */
  476. static int
  477. _sasl_client_order_mechs(const sasl_utils_t *utils,
  478. const char *mechs,
  479. int has_cb_data,
  480. char **ordered_mechs,
  481. size_t *count,
  482. int *server_can_cb)
  483. {
  484. char *list, *listp;
  485. size_t i, mechslen, start;
  486. *count = 0;
  487. *server_can_cb = 0;
  488. if (mechs == NULL || mechs[0] == '\0')
  489. return SASL_NOMECH;
  490. mechslen = strlen(mechs);
  491. listp = list = utils->malloc(mechslen + 1);
  492. if (list == NULL)
  493. return SASL_NOMEM;
  494. /* As per RFC 4422:
  495. * SASL mechanism allowable characters are "AZ-_"
  496. * separators can be any other characters and of any length
  497. * even variable lengths between.
  498. *
  499. * But for convenience we accept lowercase ASCII.
  500. *
  501. * Apps should be encouraged to simply use space or comma space
  502. * though
  503. */
  504. #define ismechchar(c) (isalnum((c)) || (c) == '_' || (c) == '-')
  505. do {
  506. for (i = start = 0; i <= mechslen; i++) {
  507. if (!ismechchar(mechs[i])) {
  508. const char *mechp = &mechs[start];
  509. size_t len = i - start;
  510. if (len != 0 &&
  511. _mech_plus_p(mechp, len) == has_cb_data) {
  512. memcpy(listp, mechp, len);
  513. listp[len] = '\0';
  514. listp += len + 1;
  515. (*count)++;
  516. if (*server_can_cb == 0 && has_cb_data)
  517. *server_can_cb = 1;
  518. }
  519. start = i+1;
  520. }
  521. }
  522. if (has_cb_data)
  523. has_cb_data = 0;
  524. else
  525. break;
  526. } while (1);
  527. if (*count == 0) {
  528. utils->free(list);
  529. return SASL_NOMECH;
  530. }
  531. *ordered_mechs = list;
  532. return SASL_OK;
  533. }
  534. static INLINE int
  535. _sasl_cbinding_disp(sasl_client_params_t *cparams,
  536. int mech_nego,
  537. int server_can_cb,
  538. sasl_cbinding_disp_t *cbindingdisp)
  539. {
  540. /*
  541. * If negotiating mechanisms, then we fail immediately if the
  542. * client requires channel binding and the server does not
  543. * advertise support. Otherwise we send "y" (which later will
  544. * become "p" if we select a supporting mechanism).
  545. *
  546. * If the client explicitly selected a mechanism, then we only
  547. * send channel bindings if they're marked critical.
  548. */
  549. *cbindingdisp = SASL_CB_DISP_NONE;
  550. if (SASL_CB_PRESENT(cparams)) {
  551. if (mech_nego) {
  552. if (!server_can_cb && SASL_CB_CRITICAL(cparams)) {
  553. return SASL_NOMECH;
  554. } else {
  555. *cbindingdisp = SASL_CB_DISP_WANT;
  556. }
  557. } else if (SASL_CB_CRITICAL(cparams)) {
  558. *cbindingdisp = SASL_CB_DISP_USED;
  559. }
  560. }
  561. return SASL_OK;
  562. }
  563. /* select a mechanism for a connection
  564. * mechlist -- mechanisms server has available (punctuation ignored)
  565. * secret -- optional secret from previous session
  566. * output:
  567. * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
  568. * clientout -- the initial client response to send to the server
  569. * mech -- set to mechanism name
  570. *
  571. * Returns:
  572. * SASL_OK -- success
  573. * SASL_NOMEM -- not enough memory
  574. * SASL_NOMECH -- no mechanism meets requested properties
  575. * SASL_INTERACT -- user interaction needed to fill in prompt_need list
  576. */
  577. /*
  578. * SASL mechanism allowable characters are "AZ-_"
  579. * separators can be any other characters and of any length
  580. * even variable lengths between.
  581. *
  582. * But for convenience we accept lowercase ASCII.
  583. *
  584. * Apps should be encouraged to simply use space or comma space
  585. * though
  586. */
  587. int sasl_client_start(sasl_conn_t *conn,
  588. const char *mechlist,
  589. sasl_interact_t **prompt_need,
  590. const char **clientout,
  591. unsigned *clientoutlen,
  592. const char **mech)
  593. {
  594. sasl_client_conn_t *c_conn = (sasl_client_conn_t *) conn;
  595. char *ordered_mechs = NULL, *name;
  596. cmechanism_t *m = NULL, *bestm = NULL;
  597. size_t i, list_len, name_len;
  598. sasl_ssf_t minssf = 0;
  599. int result, server_can_cb = 0;
  600. sasl_cbinding_disp_t cbindingdisp;
  601. sasl_cbinding_disp_t cur_cbindingdisp;
  602. sasl_cbinding_disp_t best_cbindingdisp = SASL_CB_DISP_NONE;
  603. if (_sasl_client_active == 0) return SASL_NOTINIT;
  604. if (!conn) return SASL_BADPARAM;
  605. /* verify parameters */
  606. if (mechlist == NULL) {
  607. PARAMERROR(conn);
  608. }
  609. /* if prompt_need != NULL we've already been here
  610. and just need to do the continue step again */
  611. /* do a step */
  612. /* FIXME: Hopefully they only give us our own prompt_need back */
  613. if (prompt_need && *prompt_need != NULL) {
  614. goto dostep;
  615. }
  616. if (conn->props.min_ssf < conn->external.ssf) {
  617. minssf = 0;
  618. } else {
  619. minssf = conn->props.min_ssf - conn->external.ssf;
  620. }
  621. /* Order mechanisms so -PLUS are preferred */
  622. result = _sasl_client_order_mechs(c_conn->cparams->utils,
  623. mechlist,
  624. SASL_CB_PRESENT(c_conn->cparams),
  625. &ordered_mechs,
  626. &list_len,
  627. &server_can_cb);
  628. if (result != 0)
  629. goto done;
  630. /*
  631. * Determine channel binding disposition based on whether we
  632. * are doing mechanism negotiation and whether server supports
  633. * channel bindings.
  634. */
  635. result = _sasl_cbinding_disp(c_conn->cparams,
  636. (list_len > 1),
  637. server_can_cb,
  638. &cbindingdisp);
  639. if (result != 0)
  640. goto done;
  641. /* for each mechanism in client's list */
  642. for (m = c_conn->mech_list; !bestm && m != NULL; m = m->next) {
  643. for (i = 0, name = ordered_mechs; i < list_len; i++, name += name_len + 1) {
  644. unsigned myflags;
  645. int plus;
  646. name_len = strlen(name);
  647. if (!_sasl_is_equal_mech(name, m->m.plug->mech_name, name_len, &plus)) {
  648. continue;
  649. }
  650. /* Do we have the prompts for it? */
  651. if (!have_prompts(conn, m->m.plug))
  652. break;
  653. /* Is it strong enough? */
  654. if (minssf > m->m.plug->max_ssf)
  655. break;
  656. myflags = conn->props.security_flags;
  657. /* if there's an external layer with a better SSF then this is no
  658. * longer considered a plaintext mechanism
  659. */
  660. if ((conn->props.min_ssf <= conn->external.ssf) &&
  661. (conn->external.ssf > 1)) {
  662. myflags &= ~SASL_SEC_NOPLAINTEXT;
  663. }
  664. /* Does it meet our security properties? */
  665. if (((myflags ^ m->m.plug->security_flags) & myflags) != 0) {
  666. break;
  667. }
  668. /* Can we meet it's features? */
  669. if (cbindingdisp == SASL_CB_DISP_USED &&
  670. !(m->m.plug->features & SASL_FEAT_CHANNEL_BINDING)) {
  671. break;
  672. }
  673. if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
  674. && !conn->serverFQDN) {
  675. break;
  676. }
  677. /* Can it meet our features? */
  678. if ((conn->flags & SASL_NEED_PROXY) &&
  679. !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
  680. break;
  681. }
  682. if ((conn->flags & SASL_NEED_HTTP) &&
  683. !(m->m.plug->features & SASL_FEAT_SUPPORTS_HTTP)) {
  684. break;
  685. }
  686. if (SASL_CB_PRESENT(c_conn->cparams) && plus) {
  687. cur_cbindingdisp = SASL_CB_DISP_USED;
  688. } else {
  689. cur_cbindingdisp = cbindingdisp;
  690. }
  691. if (mech) {
  692. *mech = m->m.plug->mech_name;
  693. }
  694. /* Since the list of client mechs is ordered by preference/strength,
  695. the first mech in our list that is available on the server and
  696. meets our security properties and features is the "best" */
  697. best_cbindingdisp = cur_cbindingdisp;
  698. bestm = m;
  699. break;
  700. }
  701. }
  702. if (bestm == NULL) {
  703. sasl_seterror(conn, 0, "No worthy mechs found");
  704. result = SASL_NOMECH;
  705. goto done;
  706. }
  707. /* make (the rest of) cparams */
  708. c_conn->cparams->service = conn->service;
  709. c_conn->cparams->servicelen = (unsigned) strlen(conn->service);
  710. if (conn->serverFQDN) {
  711. c_conn->cparams->serverFQDN = conn->serverFQDN;
  712. c_conn->cparams->slen = (unsigned) strlen(conn->serverFQDN);
  713. }
  714. c_conn->cparams->clientFQDN = c_conn->clientFQDN;
  715. c_conn->cparams->clen = (unsigned) strlen(c_conn->clientFQDN);
  716. c_conn->cparams->external_ssf = conn->external.ssf;
  717. c_conn->cparams->props = conn->props;
  718. c_conn->cparams->cbindingdisp = best_cbindingdisp;
  719. c_conn->mech = bestm;
  720. /* init that plugin */
  721. result = c_conn->mech->m.plug->mech_new(c_conn->mech->m.plug->glob_context,
  722. c_conn->cparams,
  723. &(conn->context));
  724. if (result != SASL_OK) goto done;
  725. /* do a step -- but only if we can do a client-send-first */
  726. dostep:
  727. if(clientout) {
  728. if(c_conn->mech->m.plug->features & SASL_FEAT_SERVER_FIRST) {
  729. *clientout = NULL;
  730. *clientoutlen = 0;
  731. result = SASL_CONTINUE;
  732. } else {
  733. result = sasl_client_step(conn, NULL, 0, prompt_need,
  734. clientout, clientoutlen);
  735. }
  736. }
  737. else
  738. result = SASL_CONTINUE;
  739. done:
  740. if (ordered_mechs != NULL)
  741. c_conn->cparams->utils->free(ordered_mechs);
  742. RETURN(conn, result);
  743. }
  744. /* do a single authentication step.
  745. * serverin -- the server message received by the client, MUST have a NUL
  746. * sentinel, not counted by serverinlen
  747. * output:
  748. * prompt_need -- on SASL_INTERACT, list of prompts needed to continue
  749. * clientout -- the client response to send to the server
  750. *
  751. * returns:
  752. * SASL_OK -- success
  753. * SASL_INTERACT -- user interaction needed to fill in prompt_need list
  754. * SASL_BADPROT -- server protocol incorrect/cancelled
  755. * SASL_BADSERV -- server failed mutual auth
  756. */
  757. int sasl_client_step(sasl_conn_t *conn,
  758. const char *serverin,
  759. unsigned serverinlen,
  760. sasl_interact_t **prompt_need,
  761. const char **clientout,
  762. unsigned *clientoutlen)
  763. {
  764. sasl_client_conn_t *c_conn= (sasl_client_conn_t *) conn;
  765. int result;
  766. if (_sasl_client_active == 0) return SASL_NOTINIT;
  767. if (!conn) return SASL_BADPARAM;
  768. /* check parameters */
  769. if ((serverin==NULL) && (serverinlen>0))
  770. PARAMERROR(conn);
  771. /* Don't do another step if the plugin told us that we're done */
  772. if (conn->oparams.doneflag) {
  773. _sasl_log(conn, SASL_LOG_ERR, "attempting client step after doneflag");
  774. return SASL_FAIL;
  775. }
  776. if(clientout) *clientout = NULL;
  777. if(clientoutlen) *clientoutlen = 0;
  778. /* do a step */
  779. result = c_conn->mech->m.plug->mech_step(conn->context,
  780. c_conn->cparams,
  781. serverin,
  782. serverinlen,
  783. prompt_need,
  784. clientout, clientoutlen,
  785. &conn->oparams);
  786. if (result == SASL_OK) {
  787. /* So we're done on this end, but if both
  788. * 1. the mech does server-send-last
  789. * 2. the protocol does not
  790. * we need to return no data */
  791. if(!*clientout && !(conn->flags & SASL_SUCCESS_DATA)) {
  792. *clientout = "";
  793. *clientoutlen = 0;
  794. }
  795. if(!conn->oparams.maxoutbuf) {
  796. conn->oparams.maxoutbuf = conn->props.maxbufsize;
  797. }
  798. if(conn->oparams.user == NULL || conn->oparams.authid == NULL) {
  799. sasl_seterror(conn, 0,
  800. "mech did not call canon_user for both authzid and authid");
  801. result = SASL_BADPROT;
  802. }
  803. }
  804. RETURN(conn,result);
  805. }
  806. /* returns the length of all the mechanisms
  807. * added up
  808. */
  809. static unsigned mech_names_len(cmechanism_t *mech_list)
  810. {
  811. cmechanism_t *listptr;
  812. unsigned result = 0;
  813. for (listptr = mech_list;
  814. listptr;
  815. listptr = listptr->next)
  816. result += (unsigned) strlen(listptr->m.plug->mech_name);
  817. return result;
  818. }
  819. int _sasl_client_listmech(sasl_conn_t *conn,
  820. const char *prefix,
  821. const char *sep,
  822. const char *suffix,
  823. const char **result,
  824. unsigned *plen,
  825. int *pcount)
  826. {
  827. sasl_client_conn_t *c_conn = (sasl_client_conn_t *)conn;
  828. cmechanism_t *m = NULL;
  829. sasl_ssf_t minssf = 0;
  830. int ret;
  831. size_t resultlen;
  832. int flag;
  833. const char *mysep;
  834. if (_sasl_client_active == 0) return SASL_NOTINIT;
  835. if (!conn) return SASL_BADPARAM;
  836. if (conn->type != SASL_CONN_CLIENT) PARAMERROR(conn);
  837. if (! result)
  838. PARAMERROR(conn);
  839. if (plen != NULL)
  840. *plen = 0;
  841. if (pcount != NULL)
  842. *pcount = 0;
  843. if (sep) {
  844. mysep = sep;
  845. } else {
  846. mysep = " ";
  847. }
  848. if (conn->props.min_ssf < conn->external.ssf) {
  849. minssf = 0;
  850. } else {
  851. minssf = conn->props.min_ssf - conn->external.ssf;
  852. }
  853. if (!c_conn->mech_list || c_conn->mech_length <= 0) {
  854. INTERROR(conn, SASL_NOMECH);
  855. }
  856. resultlen = (prefix ? strlen(prefix) : 0)
  857. + (strlen(mysep) * (c_conn->mech_length - 1))
  858. + mech_names_len(c_conn->mech_list)
  859. + (suffix ? strlen(suffix) : 0)
  860. + 1;
  861. ret = _buf_alloc(&conn->mechlist_buf,
  862. &conn->mechlist_buf_len,
  863. resultlen);
  864. if (ret != SASL_OK) MEMERROR(conn);
  865. if (prefix) {
  866. strcpy (conn->mechlist_buf,prefix);
  867. } else {
  868. *(conn->mechlist_buf) = '\0';
  869. }
  870. flag = 0;
  871. for (m = c_conn->mech_list; m != NULL; m = m->next) {
  872. /* do we have the prompts for it? */
  873. if (!have_prompts(conn, m->m.plug)) {
  874. continue;
  875. }
  876. /* is it strong enough? */
  877. if (minssf > m->m.plug->max_ssf) {
  878. continue;
  879. }
  880. /* does it meet our security properties? */
  881. if (((conn->props.security_flags ^ m->m.plug->security_flags)
  882. & conn->props.security_flags) != 0) {
  883. continue;
  884. }
  885. /* Can we meet it's features? */
  886. if ((m->m.plug->features & SASL_FEAT_NEEDSERVERFQDN)
  887. && !conn->serverFQDN) {
  888. continue;
  889. }
  890. /* Can it meet our features? */
  891. if ((conn->flags & SASL_NEED_PROXY) &&
  892. !(m->m.plug->features & SASL_FEAT_ALLOWS_PROXY)) {
  893. continue;
  894. }
  895. /* Okay, we like it, add it to the list! */
  896. if (pcount != NULL)
  897. (*pcount)++;
  898. /* print seperator */
  899. if (flag) {
  900. strcat(conn->mechlist_buf, mysep);
  901. } else {
  902. flag = 1;
  903. }
  904. /* now print the mechanism name */
  905. strcat(conn->mechlist_buf, m->m.plug->mech_name);
  906. }
  907. if (suffix)
  908. strcat(conn->mechlist_buf,suffix);
  909. if (plen!=NULL)
  910. *plen = (unsigned) strlen(conn->mechlist_buf);
  911. *result = conn->mechlist_buf;
  912. return SASL_OK;
  913. }
  914. sasl_string_list_t *_sasl_client_mechs(void)
  915. {
  916. cmechanism_t *listptr;
  917. sasl_string_list_t *retval = NULL, *next=NULL;
  918. if(!_sasl_client_active) return NULL;
  919. /* make list */
  920. for (listptr = cmechlist->mech_list; listptr; listptr = listptr->next) {
  921. next = sasl_ALLOC(sizeof(sasl_string_list_t));
  922. if(!next && !retval) return NULL;
  923. else if(!next) {
  924. next = retval->next;
  925. do {
  926. sasl_FREE(retval);
  927. retval = next;
  928. next = retval->next;
  929. } while(next);
  930. return NULL;
  931. }
  932. next->d = listptr->m.plug->mech_name;
  933. if(!retval) {
  934. next->next = NULL;
  935. retval = next;
  936. } else {
  937. next->next = retval;
  938. retval = next;
  939. }
  940. }
  941. return retval;
  942. }
  943. /* It would be nice if we can show other information like Author, Company, Year, plugin version */
  944. static void
  945. _sasl_print_mechanism (
  946. client_sasl_mechanism_t *m,
  947. sasl_info_callback_stage_t stage,
  948. void *rock __attribute__((unused))
  949. )
  950. {
  951. char delimiter;
  952. if (stage == SASL_INFO_LIST_START) {
  953. printf ("List of client plugins follows\n");
  954. return;
  955. } else if (stage == SASL_INFO_LIST_END) {
  956. return;
  957. }
  958. /* Process the mechanism */
  959. printf ("Plugin \"%s\" ", m->plugname);
  960. /* There is no delay loading for client side plugins */
  961. printf ("[loaded]");
  962. printf (", \tAPI version: %d\n", m->version);
  963. if (m->plug != NULL) {
  964. printf ("\tSASL mechanism: %s, best SSF: %d\n",
  965. m->plug->mech_name,
  966. m->plug->max_ssf);
  967. printf ("\tsecurity flags:");
  968. delimiter = ' ';
  969. if (m->plug->security_flags & SASL_SEC_NOANONYMOUS) {
  970. printf ("%cNO_ANONYMOUS", delimiter);
  971. delimiter = '|';
  972. }
  973. if (m->plug->security_flags & SASL_SEC_NOPLAINTEXT) {
  974. printf ("%cNO_PLAINTEXT", delimiter);
  975. delimiter = '|';
  976. }
  977. if (m->plug->security_flags & SASL_SEC_NOACTIVE) {
  978. printf ("%cNO_ACTIVE", delimiter);
  979. delimiter = '|';
  980. }
  981. if (m->plug->security_flags & SASL_SEC_NODICTIONARY) {
  982. printf ("%cNO_DICTIONARY", delimiter);
  983. delimiter = '|';
  984. }
  985. if (m->plug->security_flags & SASL_SEC_FORWARD_SECRECY) {
  986. printf ("%cFORWARD_SECRECY", delimiter);
  987. delimiter = '|';
  988. }
  989. if (m->plug->security_flags & SASL_SEC_PASS_CREDENTIALS) {
  990. printf ("%cPASS_CREDENTIALS", delimiter);
  991. delimiter = '|';
  992. }
  993. if (m->plug->security_flags & SASL_SEC_MUTUAL_AUTH) {
  994. printf ("%cMUTUAL_AUTH", delimiter);
  995. delimiter = '|';
  996. }
  997. printf ("\n\tfeatures:");
  998. delimiter = ' ';
  999. if (m->plug->features & SASL_FEAT_WANT_CLIENT_FIRST) {
  1000. printf ("%cWANT_CLIENT_FIRST", delimiter);
  1001. delimiter = '|';
  1002. }
  1003. if (m->plug->features & SASL_FEAT_SERVER_FIRST) {
  1004. printf ("%cSERVER_FIRST", delimiter);
  1005. delimiter = '|';
  1006. }
  1007. if (m->plug->features & SASL_FEAT_ALLOWS_PROXY) {
  1008. printf ("%cPROXY_AUTHENTICATION", delimiter);
  1009. delimiter = '|';
  1010. }
  1011. if (m->plug->features & SASL_FEAT_NEEDSERVERFQDN) {
  1012. printf ("%cNEED_SERVER_FQDN", delimiter);
  1013. delimiter = '|';
  1014. }
  1015. if (m->plug->features & SASL_FEAT_GSS_FRAMING) {
  1016. printf ("%cGSS_FRAMING", delimiter);
  1017. delimiter = '|';
  1018. }
  1019. if (m->plug->features & SASL_FEAT_CHANNEL_BINDING) {
  1020. printf ("%cCHANNEL_BINDING", delimiter);
  1021. delimiter = '|';
  1022. }
  1023. if (m->plug->features & SASL_FEAT_SUPPORTS_HTTP) {
  1024. printf ("%cSUPPORTS_HTTP", delimiter);
  1025. delimiter = '|';
  1026. }
  1027. }
  1028. /* Delay loading is not supported for the client side plugins:
  1029. if (m->f) {
  1030. printf ("\n\twill be loaded from \"%s\"", m->f);
  1031. }
  1032. */
  1033. printf ("\n");
  1034. }
  1035. /* Dump information about available client plugins */
  1036. int sasl_client_plugin_info (
  1037. const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
  1038. sasl_client_info_callback_t *info_cb,
  1039. void *info_cb_rock
  1040. )
  1041. {
  1042. cmechanism_t *m;
  1043. client_sasl_mechanism_t plug_data;
  1044. char * cur_mech;
  1045. char * mech_list = NULL;
  1046. char * p;
  1047. if (info_cb == NULL) {
  1048. info_cb = _sasl_print_mechanism;
  1049. }
  1050. if (cmechlist != NULL) {
  1051. info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
  1052. if (c_mech_list == NULL) {
  1053. m = cmechlist->mech_list; /* m point to beginning of the list */
  1054. while (m != NULL) {
  1055. memcpy (&plug_data, &m->m, sizeof(plug_data));
  1056. info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
  1057. m = m->next;
  1058. }
  1059. } else {
  1060. mech_list = strdup (c_mech_list);
  1061. cur_mech = mech_list;
  1062. while (cur_mech != NULL) {
  1063. p = strchr (cur_mech, ' ');
  1064. if (p != NULL) {
  1065. *p = '\0';
  1066. p++;
  1067. }
  1068. m = cmechlist->mech_list; /* m point to beginning of the list */
  1069. while (m != NULL) {
  1070. if (strcasecmp (cur_mech, m->m.plug->mech_name) == 0) {
  1071. memcpy (&plug_data, &m->m, sizeof(plug_data));
  1072. info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
  1073. }
  1074. m = m->next;
  1075. }
  1076. cur_mech = p;
  1077. }
  1078. free (mech_list);
  1079. }
  1080. info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
  1081. return (SASL_OK);
  1082. }
  1083. return (SASL_NOTINIT);
  1084. }