auxprop.c 31 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202
  1. /* auxprop.c - auxilliary property support
  2. * Rob Siemborski
  3. */
  4. /*
  5. * Copyright (c) 1998-2016 Carnegie Mellon University. All rights reserved.
  6. *
  7. * Redistribution and use in source and binary forms, with or without
  8. * modification, are permitted provided that the following conditions
  9. * are met:
  10. *
  11. * 1. Redistributions of source code must retain the above copyright
  12. * notice, this list of conditions and the following disclaimer.
  13. *
  14. * 2. Redistributions in binary form must reproduce the above copyright
  15. * notice, this list of conditions and the following disclaimer in
  16. * the documentation and/or other materials provided with the
  17. * distribution.
  18. *
  19. * 3. The name "Carnegie Mellon University" must not be used to
  20. * endorse or promote products derived from this software without
  21. * prior written permission. For permission or any other legal
  22. * details, please contact
  23. * Carnegie Mellon University
  24. * Center for Technology Transfer and Enterprise Creation
  25. * 4615 Forbes Avenue
  26. * Suite 302
  27. * Pittsburgh, PA 15213
  28. * (412) 268-7393, fax: (412) 268-7395
  29. * innovation@andrew.cmu.edu
  30. *
  31. * 4. Redistributions of any form whatsoever must retain the following
  32. * acknowledgment:
  33. * "This product includes software developed by Computing Services
  34. * at Carnegie Mellon University (http://www.cmu.edu/computing/)."
  35. *
  36. * CARNEGIE MELLON UNIVERSITY DISCLAIMS ALL WARRANTIES WITH REGARD TO
  37. * THIS SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  38. * AND FITNESS, IN NO EVENT SHALL CARNEGIE MELLON UNIVERSITY BE LIABLE
  39. * FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  40. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN
  41. * AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING
  42. * OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  43. */
  44. #include <config.h>
  45. #include <sasl.h>
  46. #include <prop.h>
  47. #include <ctype.h>
  48. #include <stdio.h>
  49. #include "saslint.h"
  50. struct proppool
  51. {
  52. struct proppool *next;
  53. size_t size; /* Size of Block */
  54. size_t unused; /* Space unused in this pool between end
  55. * of char** area and beginning of char* area */
  56. char data[1]; /* Variable Sized */
  57. };
  58. struct propctx {
  59. struct propval *values;
  60. struct propval *prev_val; /* Previous value used by set/setvalues */
  61. unsigned used_values, allocated_values;
  62. char *data_end; /* Bottom of string area in current pool */
  63. char **list_end; /* Top of list area in current pool */
  64. struct proppool *mem_base;
  65. struct proppool *mem_cur;
  66. };
  67. typedef struct auxprop_plug_list
  68. {
  69. struct auxprop_plug_list *next;
  70. const sasl_auxprop_plug_t *plug;
  71. } auxprop_plug_list_t;
  72. static auxprop_plug_list_t *auxprop_head = NULL;
  73. static struct proppool *alloc_proppool(size_t size)
  74. {
  75. struct proppool *ret;
  76. /* minus 1 for the one that is already a part of the array
  77. * in the struct */
  78. size_t total_size = sizeof(struct proppool) + size - 1;
  79. ret = sasl_ALLOC(total_size);
  80. if(!ret) return NULL;
  81. memset(ret, 0, total_size);
  82. ret->size = ret->unused = size;
  83. return ret;
  84. }
  85. /* Resize a proppool. Invalidates the unused value for this pool */
  86. static struct proppool *resize_proppool(struct proppool *pool, size_t size)
  87. {
  88. struct proppool *ret;
  89. if(pool->size >= size) return pool;
  90. ret = sasl_REALLOC(pool, sizeof(struct proppool) + size);
  91. if(!ret) return NULL;
  92. ret->size = size;
  93. return ret;
  94. }
  95. static int prop_init(struct propctx *ctx, unsigned estimate)
  96. {
  97. const unsigned VALUES_SIZE = PROP_DEFAULT * sizeof(struct propval);
  98. ctx->mem_base = alloc_proppool(VALUES_SIZE + estimate);
  99. if(!ctx->mem_base) return SASL_NOMEM;
  100. ctx->mem_cur = ctx->mem_base;
  101. ctx->values = (struct propval *)ctx->mem_base->data;
  102. ctx->mem_base->unused = ctx->mem_base->size - VALUES_SIZE;
  103. ctx->allocated_values = PROP_DEFAULT;
  104. ctx->used_values = 0;
  105. ctx->data_end = ctx->mem_base->data + ctx->mem_base->size;
  106. ctx->list_end = (char **)(ctx->mem_base->data + VALUES_SIZE);
  107. ctx->prev_val = NULL;
  108. return SASL_OK;
  109. }
  110. /* create a property context
  111. * estimate -- an estimate of the storage needed for requests & responses
  112. * 0 will use module default
  113. * returns NULL on error
  114. */
  115. struct propctx *prop_new(unsigned estimate)
  116. {
  117. struct propctx *new_ctx;
  118. if(!estimate) estimate = PROP_DEFAULT * 255;
  119. new_ctx = sasl_ALLOC(sizeof(struct propctx));
  120. if(!new_ctx) return NULL;
  121. if(prop_init(new_ctx, estimate) != SASL_OK) {
  122. prop_dispose(&new_ctx);
  123. }
  124. return new_ctx;
  125. }
  126. /* create new propctx which duplicates the contents of an existing propctx
  127. * returns -1 on error
  128. */
  129. int prop_dup(struct propctx *src_ctx, struct propctx **dst_ctx)
  130. {
  131. struct proppool *pool;
  132. struct propctx *retval = NULL;
  133. unsigned i;
  134. int result;
  135. unsigned total_size = 0;
  136. size_t values_size;
  137. if(!src_ctx || !dst_ctx) return SASL_BADPARAM;
  138. /* What is the total allocated size of src_ctx? */
  139. pool = src_ctx->mem_base;
  140. while(pool) {
  141. total_size += (unsigned) pool->size;
  142. pool = pool->next;
  143. }
  144. /* allocate the new context */
  145. retval = prop_new(total_size);
  146. if(!retval) return SASL_NOMEM;
  147. retval->used_values = src_ctx->used_values;
  148. retval->allocated_values = src_ctx->used_values + 1;
  149. values_size = (retval->allocated_values * sizeof(struct propval));
  150. retval->mem_base->unused = retval->mem_base->size - values_size;
  151. retval->list_end = (char **)(retval->mem_base->data + values_size);
  152. /* data_end should still be OK */
  153. /* Now dup the values */
  154. for(i=0; i<src_ctx->used_values; i++) {
  155. retval->values[i].name = src_ctx->values[i].name;
  156. result = prop_setvals(retval, retval->values[i].name,
  157. src_ctx->values[i].values);
  158. if(result != SASL_OK)
  159. goto fail;
  160. }
  161. retval->prev_val = src_ctx->prev_val;
  162. *dst_ctx = retval;
  163. return SASL_OK;
  164. fail:
  165. if(retval) prop_dispose(&retval);
  166. return result;
  167. }
  168. /*
  169. * dispose of property context
  170. * ctx -- is disposed and set to NULL; noop if ctx or *ctx is NULL
  171. */
  172. void prop_dispose(struct propctx **ctx)
  173. {
  174. struct proppool *tmp;
  175. if(!ctx || !*ctx) return;
  176. while((*ctx)->mem_base) {
  177. tmp = (*ctx)->mem_base;
  178. (*ctx)->mem_base = tmp->next;
  179. sasl_FREE(tmp);
  180. }
  181. sasl_FREE(*ctx);
  182. *ctx = NULL;
  183. return;
  184. }
  185. /* Add property names to request
  186. * ctx -- context from prop_new()
  187. * names -- list of property names; must persist until context freed
  188. * or requests cleared
  189. *
  190. * NOTE: may clear values from context as side-effect
  191. * returns -1 on error
  192. */
  193. int prop_request(struct propctx *ctx, const char **names)
  194. {
  195. unsigned i, new_values, total_values;
  196. if(!ctx || !names) return SASL_BADPARAM;
  197. /* Count how many we need to add */
  198. for(new_values=0; names[new_values]; new_values++);
  199. /* Do we need to add ANY? */
  200. if(!new_values) return SASL_OK;
  201. /* We always want at least one extra to mark the end of the array */
  202. total_values = new_values + ctx->used_values + 1;
  203. /* Do we need to increase the size of our propval table? */
  204. if(total_values > ctx->allocated_values) {
  205. unsigned max_in_pool;
  206. /* Do we need a larger base pool? */
  207. max_in_pool = (unsigned) (ctx->mem_base->size / sizeof(struct propval));
  208. if(total_values <= max_in_pool) {
  209. /* Don't increase the size of the base pool, just use what
  210. we need */
  211. ctx->allocated_values = total_values;
  212. ctx->mem_base->unused =
  213. ctx->mem_base->size - (sizeof(struct propval)
  214. * ctx->allocated_values);
  215. } else {
  216. /* We need to allocate more! */
  217. unsigned new_alloc_length;
  218. size_t new_size;
  219. new_alloc_length = 2 * ctx->allocated_values;
  220. while(total_values > new_alloc_length) {
  221. new_alloc_length *= 2;
  222. }
  223. new_size = new_alloc_length * sizeof(struct propval);
  224. ctx->mem_base = resize_proppool(ctx->mem_base, new_size);
  225. if(!ctx->mem_base) {
  226. ctx->values = NULL;
  227. ctx->allocated_values = ctx->used_values = 0;
  228. return SASL_NOMEM;
  229. }
  230. /* It worked! Update the structure! */
  231. ctx->values = (struct propval *)ctx->mem_base->data;
  232. ctx->allocated_values = new_alloc_length;
  233. ctx->mem_base->unused = ctx->mem_base->size
  234. - sizeof(struct propval) * ctx->allocated_values;
  235. }
  236. /* Clear out new propvals */
  237. memset(&(ctx->values[ctx->used_values]), 0,
  238. sizeof(struct propval) * (ctx->allocated_values - ctx->used_values));
  239. /* Finish updating the context -- we've extended the list! */
  240. /* ctx->list_end = (char **)(ctx->values + ctx->allocated_values); */
  241. /* xxx test here */
  242. ctx->list_end = (char **)(ctx->values + total_values);
  243. }
  244. /* Now do the copy, or referencing rather */
  245. for(i=0;i<new_values;i++) {
  246. unsigned j, flag;
  247. flag = 0;
  248. /* Check for dups */
  249. for(j=0;j<ctx->used_values;j++) {
  250. if(!strcmp(ctx->values[j].name, names[i])) {
  251. flag = 1;
  252. break;
  253. }
  254. }
  255. /* We already have it... skip! */
  256. if(flag) continue;
  257. ctx->values[ctx->used_values++].name = names[i];
  258. }
  259. prop_clear(ctx, 0);
  260. return SASL_OK;
  261. }
  262. /* return array of struct propval from the context
  263. * return value persists until next call to
  264. * prop_request, prop_clear or prop_dispose on context
  265. */
  266. const struct propval *prop_get(struct propctx *ctx)
  267. {
  268. if(!ctx) return NULL;
  269. return ctx->values;
  270. }
  271. /* Fill in an array of struct propval based on a list of property names
  272. * return value persists until next call to
  273. * prop_request, prop_clear or prop_dispose on context
  274. * returns -1 on error (no properties ever requested, ctx NULL, etc)
  275. * returns number of matching properties which were found (values != NULL)
  276. * if a name requested here was never requested by a prop_request, then
  277. * the name field of the associated vals entry will be set to NULL
  278. */
  279. int prop_getnames(struct propctx *ctx, const char **names,
  280. struct propval *vals)
  281. {
  282. int found_names = 0;
  283. struct propval *cur = vals;
  284. const char **curname;
  285. if(!ctx || !names || !vals) return SASL_BADPARAM;
  286. for(curname = names; *curname; curname++) {
  287. struct propval *val;
  288. for(val = ctx->values; val->name; val++) {
  289. if(!strcmp(*curname,val->name)) {
  290. found_names++;
  291. memcpy(cur, val, sizeof(struct propval));
  292. goto next;
  293. }
  294. }
  295. /* If we are here, we didn't find it */
  296. memset(cur, 0, sizeof(struct propval));
  297. next:
  298. cur++;
  299. }
  300. return found_names;
  301. }
  302. /* clear values and optionally requests from property context
  303. * ctx -- property context
  304. * requests -- 0 = don't clear requests, 1 = clear requests
  305. */
  306. void prop_clear(struct propctx *ctx, int requests)
  307. {
  308. struct proppool *new_pool, *tmp;
  309. unsigned i;
  310. /* We're going to need a new proppool once we reset things */
  311. new_pool = alloc_proppool(ctx->mem_base->size +
  312. (ctx->used_values+1) * sizeof(struct propval));
  313. if (new_pool == NULL) {
  314. _sasl_log(NULL, SASL_LOG_ERR, "failed to allocate memory\n");
  315. exit(1);
  316. }
  317. if(requests) {
  318. /* We're wiping the whole shebang */
  319. ctx->used_values = 0;
  320. } else {
  321. /* Need to keep around old requets */
  322. struct propval *new_values = (struct propval *)new_pool->data;
  323. for(i=0; i<ctx->used_values; i++) {
  324. new_values[i].name = ctx->values[i].name;
  325. }
  326. }
  327. while(ctx->mem_base) {
  328. tmp = ctx->mem_base;
  329. ctx->mem_base = tmp->next;
  330. sasl_FREE(tmp);
  331. }
  332. /* Update allocation-related metadata */
  333. ctx->allocated_values = ctx->used_values+1;
  334. new_pool->unused =
  335. new_pool->size - (ctx->allocated_values * sizeof(struct propval));
  336. /* Setup pointers for the values array */
  337. ctx->values = (struct propval *)new_pool->data;
  338. ctx->prev_val = NULL;
  339. /* Setup the pools */
  340. ctx->mem_base = ctx->mem_cur = new_pool;
  341. /* Reset list_end and data_end for the new memory pool */
  342. ctx->list_end =
  343. (char **)((char *)ctx->mem_base->data + ctx->allocated_values * sizeof(struct propval));
  344. ctx->data_end = (char *)ctx->mem_base->data + ctx->mem_base->size;
  345. return;
  346. }
  347. /*
  348. * erase the value of a property
  349. */
  350. void prop_erase(struct propctx *ctx, const char *name)
  351. {
  352. struct propval *val;
  353. int i;
  354. if(!ctx || !name) return;
  355. for(val = ctx->values; val->name; val++) {
  356. if(!strcmp(name,val->name)) {
  357. if(!val->values) break;
  358. /*
  359. * Yes, this is casting away the const, but
  360. * we should be okay because the only place this
  361. * memory should be is in the proppool's
  362. */
  363. for(i=0;val->values[i];i++) {
  364. memset((void *)(val->values[i]),0,strlen(val->values[i]));
  365. val->values[i] = NULL;
  366. }
  367. val->values = NULL;
  368. val->nvalues = 0;
  369. val->valsize = 0;
  370. break;
  371. }
  372. }
  373. return;
  374. }
  375. /****fetcher interfaces****/
  376. /* format the requested property names into a string
  377. * ctx -- context from prop_new()/prop_request()
  378. * sep -- separator between property names (unused if none requested)
  379. * seplen -- length of separator, if < 0 then strlen(sep) will be used
  380. * outbuf -- output buffer
  381. * outmax -- maximum length of output buffer including NUL terminator
  382. * outlen -- set to length of output string excluding NUL terminator
  383. * returns 0 on success and amount of additional space needed on failure
  384. */
  385. int prop_format(struct propctx *ctx, const char *sep, int seplen,
  386. char *outbuf, unsigned outmax, unsigned *outlen)
  387. {
  388. unsigned needed, flag = 0;
  389. struct propval *val;
  390. if (!ctx || !outbuf) return SASL_BADPARAM;
  391. if (!sep) seplen = 0;
  392. if (seplen < 0) seplen = (int) strlen(sep);
  393. /* If seplen is negative now we have overflow.
  394. But if you have a string longer than 2Gb, you are an idiot anyway */
  395. if (seplen < 0) return SASL_BADPARAM;
  396. needed = seplen * (ctx->used_values - 1);
  397. for(val = ctx->values; val->name; val++) {
  398. needed += (unsigned) strlen(val->name);
  399. }
  400. if(!outmax) return (needed + 1); /* Because of unsigned funkiness */
  401. if(needed > (outmax - 1)) return (needed - (outmax - 1));
  402. *outbuf = '\0';
  403. if(outlen) *outlen = needed;
  404. if(needed == 0) return SASL_OK;
  405. for(val = ctx->values; val->name; val++) {
  406. if(seplen && flag) {
  407. strncat(outbuf, sep, seplen);
  408. } else {
  409. flag = 1;
  410. }
  411. strcat(outbuf, val->name);
  412. }
  413. return SASL_OK;
  414. }
  415. /* add a property value to the context
  416. * ctx -- context from prop_new()/prop_request()
  417. * name -- name of property to which value will be added
  418. * if NULL, add to the same name as previous prop_set/setvals call
  419. * value -- a value for the property; will be copied into context
  420. * if NULL, remove existing values
  421. * vallen -- length of value, if <= 0 then strlen(value) will be used
  422. */
  423. int prop_set(struct propctx *ctx, const char *name,
  424. const char *value, int vallen)
  425. {
  426. struct propval *cur;
  427. if(!ctx) return SASL_BADPARAM;
  428. if(!name && !ctx->prev_val) return SASL_BADPARAM;
  429. if(name) {
  430. struct propval *val;
  431. ctx->prev_val = NULL;
  432. for(val = ctx->values; val->name; val++) {
  433. if(!strcmp(name,val->name)){
  434. ctx->prev_val = val;
  435. break;
  436. }
  437. }
  438. /* Couldn't find it! */
  439. if(!ctx->prev_val) return SASL_BADPARAM;
  440. }
  441. cur = ctx->prev_val;
  442. if(name) /* New Entry */ {
  443. unsigned nvalues = 1; /* 1 for NULL entry */
  444. const char **old_values = NULL;
  445. char **tmp, **tmp2;
  446. size_t size;
  447. if(cur->values) {
  448. if(!value) {
  449. /* If we would be adding a null value, then we are done */
  450. return SASL_OK;
  451. }
  452. old_values = cur->values;
  453. tmp = (char **)cur->values;
  454. while(*tmp) {
  455. nvalues++;
  456. tmp++;
  457. }
  458. }
  459. if(value) {
  460. nvalues++; /* for the new value */
  461. }
  462. size = nvalues * sizeof(char*);
  463. if(size > ctx->mem_cur->unused) {
  464. size_t needed;
  465. for(needed = ctx->mem_cur->size * 2; needed < size; needed *= 2);
  466. /* Allocate a new proppool */
  467. ctx->mem_cur->next = alloc_proppool(needed);
  468. if(!ctx->mem_cur->next) return SASL_NOMEM;
  469. ctx->mem_cur = ctx->mem_cur->next;
  470. ctx->list_end = (char **)ctx->mem_cur->data;
  471. ctx->data_end = ctx->mem_cur->data + needed;
  472. }
  473. /* Grab the memory */
  474. ctx->mem_cur->unused -= size;
  475. cur->values = (const char **)ctx->list_end;
  476. cur->values[nvalues - 1] = NULL;
  477. /* Finish updating the context */
  478. ctx->list_end = (char **)(cur->values + nvalues);
  479. /* If we don't have an actual value to fill in, we are done */
  480. if(!value)
  481. return SASL_OK;
  482. tmp2 = (char **)cur->values;
  483. if(old_values) {
  484. tmp = (char **)old_values;
  485. while(*tmp) {
  486. *tmp2 = *tmp;
  487. tmp++; tmp2++;
  488. }
  489. }
  490. /* Now allocate the last entry */
  491. if(vallen <= 0)
  492. size = (size_t)(strlen(value) + 1);
  493. else
  494. size = (size_t)(vallen + 1);
  495. if(size > ctx->mem_cur->unused) {
  496. size_t needed;
  497. needed = ctx->mem_cur->size * 2;
  498. while(needed < size) {
  499. needed *= 2;
  500. }
  501. /* Allocate a new proppool */
  502. ctx->mem_cur->next = alloc_proppool(needed);
  503. if(!ctx->mem_cur->next) return SASL_NOMEM;
  504. ctx->mem_cur = ctx->mem_cur->next;
  505. ctx->list_end = (char **)ctx->mem_cur->data;
  506. ctx->data_end = ctx->mem_cur->data + needed;
  507. }
  508. /* Update the data_end pointer */
  509. ctx->data_end -= size;
  510. ctx->mem_cur->unused -= size;
  511. /* Copy and setup the new value! */
  512. memcpy(ctx->data_end, value, size-1);
  513. ctx->data_end[size - 1] = '\0';
  514. cur->values[nvalues - 2] = ctx->data_end;
  515. cur->nvalues++;
  516. cur->valsize += ((unsigned) size - 1);
  517. } else /* Appending an entry */ {
  518. char **tmp;
  519. size_t size;
  520. /* If we are setting it to be NULL, we are done */
  521. if(!value) return SASL_OK;
  522. size = sizeof(char*);
  523. /* Is it in the current pool, and will it fit in the unused space? */
  524. if(size > ctx->mem_cur->unused &&
  525. (void *)cur->values > (void *)(ctx->mem_cur->data) &&
  526. (void *)cur->values < (void *)(ctx->mem_cur->data + ctx->mem_cur->size)) {
  527. /* recursively call the not-fast way */
  528. return prop_set(ctx, cur->name, value, vallen);
  529. }
  530. /* Note the invariant: the previous value list must be
  531. at the top of the CURRENT pool at this point */
  532. /* Grab the memory */
  533. ctx->mem_cur->unused -= size;
  534. ctx->list_end++;
  535. *(ctx->list_end - 1) = NULL;
  536. tmp = (ctx->list_end - 2);
  537. /* Now allocate the last entry */
  538. if(vallen <= 0)
  539. size = strlen(value) + 1;
  540. else
  541. size = vallen + 1;
  542. if(size > ctx->mem_cur->unused) {
  543. size_t needed;
  544. needed = ctx->mem_cur->size * 2;
  545. while(needed < size) {
  546. needed *= 2;
  547. }
  548. /* Allocate a new proppool */
  549. ctx->mem_cur->next = alloc_proppool(needed);
  550. if(!ctx->mem_cur->next) return SASL_NOMEM;
  551. ctx->mem_cur = ctx->mem_cur->next;
  552. ctx->list_end = (char **)ctx->mem_cur->data;
  553. ctx->data_end = ctx->mem_cur->data + needed;
  554. }
  555. /* Update the data_end pointer */
  556. ctx->data_end -= size;
  557. ctx->mem_cur->unused -= size;
  558. /* Copy and setup the new value! */
  559. memcpy(ctx->data_end, value, size-1);
  560. ctx->data_end[size - 1] = '\0';
  561. *tmp = ctx->data_end;
  562. cur->nvalues++;
  563. cur->valsize += ((unsigned) size - 1);
  564. }
  565. return SASL_OK;
  566. }
  567. /* set the values for a property
  568. * ctx -- context from prop_new()/prop_request()
  569. * name -- name of property to which value will be added
  570. * if NULL, add to the same name as previous prop_set/setvals call
  571. * values -- array of values, ending in NULL. Each value is a NUL terminated
  572. * string
  573. */
  574. int prop_setvals(struct propctx *ctx, const char *name,
  575. const char **values)
  576. {
  577. const char **val = values;
  578. int result = SASL_OK;
  579. if(!ctx) return SASL_BADPARAM;
  580. /* If they want us to add no values, we can do that */
  581. if(!values) return SASL_OK;
  582. /* Basically, use prop_set to do all our dirty work for us */
  583. if(name) {
  584. result = prop_set(ctx, name, *val, 0);
  585. val++;
  586. }
  587. for(;*val;val++) {
  588. if(result != SASL_OK) return result;
  589. result = prop_set(ctx, NULL, *val,0);
  590. }
  591. return result;
  592. }
  593. /* Request a set of auxiliary properties
  594. * conn connection context
  595. * propnames list of auxiliary property names to request ending with
  596. * NULL.
  597. *
  598. * Subsequent calls will add items to the request list. Call with NULL
  599. * to clear the request list.
  600. *
  601. * errors
  602. * SASL_OK -- success
  603. * SASL_BADPARAM -- bad count/conn parameter
  604. * SASL_NOMEM -- out of memory
  605. */
  606. int sasl_auxprop_request(sasl_conn_t *conn, const char **propnames)
  607. {
  608. int result;
  609. sasl_server_conn_t *sconn;
  610. if(!conn) return SASL_BADPARAM;
  611. if(conn->type != SASL_CONN_SERVER)
  612. PARAMERROR(conn);
  613. sconn = (sasl_server_conn_t *)conn;
  614. if(!propnames) {
  615. prop_clear(sconn->sparams->propctx,1);
  616. return SASL_OK;
  617. }
  618. result = prop_request(sconn->sparams->propctx, propnames);
  619. RETURN(conn, result);
  620. }
  621. /* Returns current auxiliary property context.
  622. * Use functions in prop.h to access content
  623. *
  624. * if authentication hasn't completed, property values may be empty/NULL
  625. *
  626. * properties not recognized by active plug-ins will be left empty/NULL
  627. *
  628. * returns NULL if conn is invalid.
  629. */
  630. struct propctx *sasl_auxprop_getctx(sasl_conn_t *conn)
  631. {
  632. sasl_server_conn_t *sconn;
  633. if(!conn || conn->type != SASL_CONN_SERVER) return NULL;
  634. sconn = (sasl_server_conn_t *)conn;
  635. return sconn->sparams->propctx;
  636. }
  637. /* add an auxiliary property plugin */
  638. int sasl_auxprop_add_plugin(const char *plugname,
  639. sasl_auxprop_init_t *auxpropfunc)
  640. {
  641. int result, out_version;
  642. auxprop_plug_list_t *new_item;
  643. sasl_auxprop_plug_t *plug;
  644. result = auxpropfunc(sasl_global_utils, SASL_AUXPROP_PLUG_VERSION,
  645. &out_version, &plug, plugname);
  646. /* Check if out_version is too old.
  647. We only support the current at the moment */
  648. if (result == SASL_OK && out_version < SASL_AUXPROP_PLUG_VERSION) {
  649. result = SASL_BADVERS;
  650. }
  651. if(result != SASL_OK) {
  652. _sasl_log(NULL, SASL_LOG_ERR, "auxpropfunc error %s\n",
  653. sasl_errstring(result, NULL, NULL));
  654. return result;
  655. }
  656. /* We require that this function is implemented */
  657. if(!plug->auxprop_lookup) return SASL_BADPROT;
  658. new_item = sasl_ALLOC(sizeof(auxprop_plug_list_t));
  659. if(!new_item) return SASL_NOMEM;
  660. /* These will load from least-important to most important */
  661. new_item->plug = plug;
  662. new_item->next = auxprop_head;
  663. auxprop_head = new_item;
  664. return SASL_OK;
  665. }
  666. void _sasl_auxprop_free()
  667. {
  668. auxprop_plug_list_t *ptr, *ptr_next;
  669. for(ptr = auxprop_head; ptr; ptr = ptr_next) {
  670. ptr_next = ptr->next;
  671. if(ptr->plug->auxprop_free)
  672. ptr->plug->auxprop_free(ptr->plug->glob_context,
  673. sasl_global_utils);
  674. sasl_FREE(ptr);
  675. }
  676. auxprop_head = NULL;
  677. }
  678. /* Return the updated account status based on the current ("so far") and
  679. the specific status returned by the latest auxprop call */
  680. static int
  681. _sasl_account_status (int current_status,
  682. int specific_status)
  683. {
  684. switch (specific_status) {
  685. case SASL_NOVERIFY:
  686. specific_status = SASL_OK;
  687. /* fall through */
  688. case SASL_OK:
  689. if (current_status == SASL_NOMECH ||
  690. current_status == SASL_NOUSER) {
  691. current_status = specific_status;
  692. }
  693. break;
  694. case SASL_NOUSER:
  695. if (current_status == SASL_NOMECH) {
  696. current_status = specific_status;
  697. }
  698. break;
  699. /* NOTE: The disabled flag sticks, unless we hit an error */
  700. case SASL_DISABLED:
  701. if (current_status == SASL_NOMECH ||
  702. current_status == SASL_NOUSER ||
  703. current_status == SASL_OK) {
  704. current_status = specific_status;
  705. }
  706. break;
  707. case SASL_NOMECH:
  708. /* ignore */
  709. break;
  710. /* SASL_UNAVAIL overrides everything */
  711. case SASL_UNAVAIL:
  712. current_status = specific_status;
  713. break;
  714. default:
  715. current_status = specific_status;
  716. break;
  717. }
  718. return (current_status);
  719. }
  720. /* Do the callbacks for auxprop lookups */
  721. int _sasl_auxprop_lookup(sasl_server_params_t *sparams,
  722. unsigned flags,
  723. const char *user, unsigned ulen)
  724. {
  725. sasl_getopt_t *getopt;
  726. int ret, found = 0;
  727. void *context;
  728. const char *plist = NULL;
  729. auxprop_plug_list_t *ptr;
  730. int result = SASL_NOMECH;
  731. if(_sasl_getcallback(sparams->utils->conn,
  732. SASL_CB_GETOPT,
  733. (sasl_callback_ft *)&getopt,
  734. &context) == SASL_OK) {
  735. ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL);
  736. if(ret != SASL_OK) plist = NULL;
  737. }
  738. if(!plist) {
  739. /* Do lookup in all plugins */
  740. /* TODO: Ideally, each auxprop plugin should be marked if its failure
  741. should be ignored or treated as a fatal error of the whole lookup. */
  742. for(ptr = auxprop_head; ptr; ptr = ptr->next) {
  743. found=1;
  744. ret = ptr->plug->auxprop_lookup(ptr->plug->glob_context,
  745. sparams, flags, user, ulen);
  746. result = _sasl_account_status (result, ret);
  747. }
  748. } else {
  749. char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL;
  750. if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return SASL_NOMEM;
  751. thisplugin = freeptr = pluginlist;
  752. /* Do lookup in all *specified* plugins, in order */
  753. while(*thisplugin) {
  754. char *p;
  755. int last=0;
  756. while(*thisplugin && isspace((int)*thisplugin)) thisplugin++;
  757. if(!(*thisplugin)) break;
  758. for(p = thisplugin;*p != '\0' && !isspace((int)*p); p++);
  759. if(*p == '\0') last = 1;
  760. else *p='\0';
  761. for(ptr = auxprop_head; ptr; ptr = ptr->next) {
  762. /* Skip non-matching plugins */
  763. if(!ptr->plug->name
  764. || strcasecmp(ptr->plug->name, thisplugin))
  765. continue;
  766. found=1;
  767. ret = ptr->plug->auxprop_lookup(ptr->plug->glob_context,
  768. sparams, flags, user, ulen);
  769. result = _sasl_account_status (result, ret);
  770. }
  771. if(last) break;
  772. thisplugin = p+1;
  773. }
  774. sasl_FREE(freeptr);
  775. }
  776. if(!found) {
  777. _sasl_log(sparams->utils->conn, SASL_LOG_DEBUG,
  778. "could not find auxprop plugin, was searching for '%s'",
  779. plist ? plist : "[all]");
  780. }
  781. return result;
  782. }
  783. /* Do the callbacks for auxprop stores */
  784. int sasl_auxprop_store(sasl_conn_t *conn,
  785. struct propctx *ctx, const char *user)
  786. {
  787. sasl_getopt_t *getopt;
  788. int ret;
  789. void *context;
  790. const char *plist = NULL;
  791. auxprop_plug_list_t *ptr;
  792. sasl_server_params_t *sparams = NULL;
  793. unsigned userlen = 0;
  794. int num_constraint_violations = 0;
  795. int total_plugins = 0;
  796. if (ctx) {
  797. if (!conn || !user)
  798. return SASL_BADPARAM;
  799. sparams = ((sasl_server_conn_t *) conn)->sparams;
  800. userlen = (unsigned) strlen(user);
  801. }
  802. /* Pickup getopt callback from the connection, if conn is not NULL */
  803. if(_sasl_getcallback(conn, SASL_CB_GETOPT, (sasl_callback_ft *)&getopt, &context) == SASL_OK) {
  804. ret = getopt(context, NULL, "auxprop_plugin", &plist, NULL);
  805. if(ret != SASL_OK) plist = NULL;
  806. }
  807. ret = SASL_OK;
  808. if(!plist) {
  809. /* Do store in all plugins */
  810. for(ptr = auxprop_head; ptr && ret == SASL_OK; ptr = ptr->next) {
  811. total_plugins++;
  812. if (ptr->plug->auxprop_store) {
  813. ret = ptr->plug->auxprop_store(ptr->plug->glob_context,
  814. sparams, ctx, user, userlen);
  815. if (ret == SASL_CONSTRAINT_VIOLAT) {
  816. ret = SASL_OK;
  817. num_constraint_violations++;
  818. }
  819. }
  820. }
  821. } else {
  822. char *pluginlist = NULL, *freeptr = NULL, *thisplugin = NULL;
  823. if(_sasl_strdup(plist, &pluginlist, NULL) != SASL_OK) return SASL_FAIL;
  824. thisplugin = freeptr = pluginlist;
  825. /* Do store in all *specified* plugins, in order */
  826. while(*thisplugin) {
  827. char *p;
  828. int last=0;
  829. while(*thisplugin && isspace((int)*thisplugin)) thisplugin++;
  830. if(!(*thisplugin)) break;
  831. for(p = thisplugin;*p != '\0' && !isspace((int)*p); p++);
  832. if(*p == '\0') last = 1;
  833. else *p='\0';
  834. for(ptr = auxprop_head; ptr && ret == SASL_OK; ptr = ptr->next) {
  835. /* Skip non-matching plugins */
  836. if((!ptr->plug->name
  837. || strcasecmp(ptr->plug->name, thisplugin)))
  838. continue;
  839. total_plugins++;
  840. if (ptr->plug->auxprop_store) {
  841. ret = ptr->plug->auxprop_store(ptr->plug->glob_context,
  842. sparams, ctx, user, userlen);
  843. if (ret == SASL_CONSTRAINT_VIOLAT) {
  844. ret = SASL_OK;
  845. num_constraint_violations++;
  846. }
  847. }
  848. }
  849. if(last) break;
  850. thisplugin = p+1;
  851. }
  852. sasl_FREE(freeptr);
  853. }
  854. if(total_plugins == 0) {
  855. _sasl_log(NULL, SASL_LOG_ERR,
  856. "could not find auxprop plugin, was searching for %s",
  857. plist ? plist : "[all]");
  858. return SASL_FAIL;
  859. } else if (total_plugins == num_constraint_violations) {
  860. ret = SASL_CONSTRAINT_VIOLAT;
  861. }
  862. return ret;
  863. }
  864. /* It would be nice if we can show other information like Author, Company, Year, plugin version */
  865. static void
  866. _sasl_print_mechanism (sasl_auxprop_plug_t *m,
  867. sasl_info_callback_stage_t stage,
  868. void *rock __attribute__((unused))
  869. )
  870. {
  871. if (stage == SASL_INFO_LIST_START) {
  872. printf ("List of auxprop plugins follows\n");
  873. return;
  874. } else if (stage == SASL_INFO_LIST_END) {
  875. return;
  876. }
  877. /* Process the mechanism */
  878. printf ("Plugin \"%s\" ", m->name);
  879. #ifdef NOT_YET
  880. switch (m->condition) {
  881. case SASL_OK:
  882. printf ("[loaded]");
  883. break;
  884. case SASL_CONTINUE:
  885. printf ("[delayed]");
  886. break;
  887. case SASL_NOUSER:
  888. printf ("[no users]");
  889. break;
  890. default:
  891. printf ("[unknown]");
  892. break;
  893. }
  894. #endif
  895. printf (", \tAPI version: %d\n", /* m->version */ SASL_AUXPROP_PLUG_VERSION);
  896. /* TODO - Update for auxprop_export, etc. */
  897. printf ("\tsupports store: %s\n",
  898. (m->auxprop_store != NULL) ? "yes" : "no"
  899. );
  900. /* No features defined yet */
  901. #ifdef NOT_YET
  902. printf ("\n\tfeatures:");
  903. #endif
  904. printf ("\n");
  905. }
  906. /* Dump information about available auxprop plugins (separate functions are
  907. used for canon and server authentication plugins) */
  908. int auxprop_plugin_info (
  909. const char *c_mech_list, /* space separated mechanism list or NULL for ALL */
  910. auxprop_info_callback_t *info_cb,
  911. void *info_cb_rock
  912. )
  913. {
  914. auxprop_plug_list_t *m;
  915. sasl_auxprop_plug_t plug_data;
  916. char * cur_mech;
  917. char *mech_list = NULL;
  918. char * p;
  919. if (info_cb == NULL) {
  920. info_cb = _sasl_print_mechanism;
  921. }
  922. if (auxprop_head != NULL) {
  923. info_cb (NULL, SASL_INFO_LIST_START, info_cb_rock);
  924. if (c_mech_list == NULL) {
  925. m = auxprop_head; /* m point to beginning of the list */
  926. while (m != NULL) {
  927. /* TODO: Need to be careful when dealing with auxprop_export, etc. */
  928. memcpy (&plug_data, m->plug, sizeof(plug_data));
  929. info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
  930. m = m->next;
  931. }
  932. } else {
  933. mech_list = strdup(c_mech_list);
  934. cur_mech = mech_list;
  935. while (cur_mech != NULL) {
  936. p = strchr (cur_mech, ' ');
  937. if (p != NULL) {
  938. *p = '\0';
  939. p++;
  940. }
  941. m = auxprop_head; /* m point to beginning of the list */
  942. while (m != NULL) {
  943. if (strcasecmp (cur_mech, m->plug->name) == 0) {
  944. memcpy (&plug_data, m->plug, sizeof(plug_data));
  945. info_cb (&plug_data, SASL_INFO_LIST_MECH, info_cb_rock);
  946. }
  947. m = m->next;
  948. }
  949. cur_mech = p;
  950. }
  951. free (mech_list);
  952. }
  953. info_cb (NULL, SASL_INFO_LIST_END, info_cb_rock);
  954. return (SASL_OK);
  955. }
  956. return (SASL_NOTINIT);
  957. }