PageRenderTime 46ms CodeModel.GetById 15ms RepoModel.GetById 0ms app.codeStats 0ms

/src/modules/rlm_attr_rewrite/rlm_attr_rewrite.c

https://github.com/mikeross/freeradius-server
C | 487 lines | 390 code | 43 blank | 54 comment | 102 complexity | 188cda92e2caa0c58b891f0198944962 MD5 | raw file
  1. /*
  2. * rlm_attr_rewrite.c
  3. *
  4. * Version: $Id$
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 2 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program; if not, write to the Free Software
  18. * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
  19. *
  20. * Copyright 2002,2006 The FreeRADIUS server project
  21. * Copyright 2002 Kostas Kalevras <kkalev@noc.ntua.gr>
  22. */
  23. #include <freeradius-devel/ident.h>
  24. RCSID("$Id$")
  25. #include <freeradius-devel/radiusd.h>
  26. #include <freeradius-devel/modules.h>
  27. #ifdef HAVE_REGEX_H
  28. # include <regex.h>
  29. #endif
  30. #define RLM_REGEX_INPACKET 0
  31. #define RLM_REGEX_INCONFIG 1
  32. #define RLM_REGEX_INREPLY 2
  33. #define RLM_REGEX_INPROXY 3
  34. #define RLM_REGEX_INPROXYREPLY 4
  35. typedef struct rlm_attr_rewrite_t {
  36. char *attribute; /* The attribute to search for */
  37. int attr_num; /* The attribute number */
  38. char *search; /* The pattern to search for */
  39. int search_len; /* The length of the search pattern */
  40. char *searchin_str; /* The VALUE_PAIR list to search in. Can be either packet,reply,proxy,proxy_reply or control (plus it's alias 'config') */
  41. char searchin; /* The same as above just coded as a number for speed */
  42. char *replace; /* The replacement */
  43. int replace_len; /* The length of the replacement string */
  44. int append; /* Switch to control append mode (1,0) */
  45. int nocase; /* Ignore case */
  46. int new_attr; /* Boolean. Do we create a new attribute or not? */
  47. int num_matches; /* Maximum number of matches */
  48. const char *name; /* The module name */
  49. } rlm_attr_rewrite_t;
  50. static const CONF_PARSER module_config[] = {
  51. { "attribute", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,attribute), NULL, NULL },
  52. { "searchfor", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,search), NULL, NULL },
  53. { "searchin", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,searchin_str), NULL, "packet" },
  54. { "replacewith", PW_TYPE_STRING_PTR, offsetof(rlm_attr_rewrite_t,replace), NULL, NULL },
  55. { "append", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,append),NULL, "no" },
  56. { "ignore_case", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,nocase), NULL, "yes" },
  57. { "new_attribute", PW_TYPE_BOOLEAN, offsetof(rlm_attr_rewrite_t,new_attr), NULL, "no" },
  58. { "max_matches", PW_TYPE_INTEGER, offsetof(rlm_attr_rewrite_t,num_matches), NULL, "10" },
  59. { NULL, -1, 0, NULL, NULL }
  60. };
  61. static int attr_rewrite_instantiate(CONF_SECTION *conf, void **instance)
  62. {
  63. rlm_attr_rewrite_t *data;
  64. DICT_ATTR *dattr;
  65. /*
  66. * Set up a storage area for instance data
  67. */
  68. data = rad_malloc(sizeof(*data));
  69. if (!data) {
  70. return -1;
  71. }
  72. memset(data, 0, sizeof(*data));
  73. /*
  74. * If the configuration parameters can't be parsed, then
  75. * fail.
  76. */
  77. if (cf_section_parse(conf, data, module_config) < 0) {
  78. free(data);
  79. return -1;
  80. }
  81. /*
  82. * Discover the attribute number of the key.
  83. */
  84. if (data->attribute == NULL) {
  85. radlog(L_ERR, "rlm_attr_rewrite: 'attribute' must be set.");
  86. return -1;
  87. }
  88. if (data->search == NULL || data->replace == NULL) {
  89. radlog(L_ERR, "rlm_attr_rewrite: search/replace strings must be set.");
  90. return -1;
  91. }
  92. data->search_len = strlen(data->search);
  93. data->replace_len = strlen(data->replace);
  94. if (data->replace_len == 0 && data->new_attr){
  95. radlog(L_ERR, "rlm_attr_rewrite: replace string must not be zero length in order to create new attribute.");
  96. return -1;
  97. }
  98. if (data->num_matches < 1 || data->num_matches > MAX_STRING_LEN) {
  99. radlog(L_ERR, "rlm_attr_rewrite: Illegal range for match number.");
  100. return -1;
  101. }
  102. if (data->searchin_str == NULL) {
  103. radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
  104. data->searchin = RLM_REGEX_INPACKET;
  105. }
  106. else{
  107. if (strcmp(data->searchin_str, "packet") == 0)
  108. data->searchin = RLM_REGEX_INPACKET;
  109. else if (strcmp(data->searchin_str, "config") == 0)
  110. data->searchin = RLM_REGEX_INCONFIG;
  111. else if (strcmp(data->searchin_str, "control") == 0)
  112. data->searchin = RLM_REGEX_INCONFIG;
  113. else if (strcmp(data->searchin_str, "reply") == 0)
  114. data->searchin = RLM_REGEX_INREPLY;
  115. else if (strcmp(data->searchin_str, "proxy") == 0)
  116. data->searchin = RLM_REGEX_INPROXY;
  117. else if (strcmp(data->searchin_str, "proxy_reply") == 0)
  118. data->searchin = RLM_REGEX_INPROXYREPLY;
  119. else {
  120. radlog(L_ERR, "rlm_attr_rewrite: Illegal searchin directive given. Assuming packet.");
  121. data->searchin = RLM_REGEX_INPACKET;
  122. }
  123. }
  124. dattr = dict_attrbyname(data->attribute);
  125. if (dattr == NULL) {
  126. radlog(L_ERR, "rlm_attr_rewrite: No such attribute %s",
  127. data->attribute);
  128. return -1;
  129. }
  130. data->attr_num = dattr->attr;
  131. /* Add the module instance name */
  132. data->name = cf_section_name2(conf); /* may be NULL */
  133. *instance = data;
  134. return 0;
  135. }
  136. static int do_attr_rewrite(void *instance, REQUEST *request)
  137. {
  138. rlm_attr_rewrite_t *data = (rlm_attr_rewrite_t *) instance;
  139. int ret = RLM_MODULE_NOOP;
  140. VALUE_PAIR *attr_vp = NULL;
  141. VALUE_PAIR *tmp = NULL;
  142. regex_t preg;
  143. regmatch_t pmatch[9];
  144. int cflags = 0;
  145. int err = 0;
  146. char done_xlat = 0;
  147. unsigned int len = 0;
  148. char err_msg[MAX_STRING_LEN];
  149. unsigned int i = 0;
  150. unsigned int j = 0;
  151. unsigned int counter = 0;
  152. char new_str[MAX_STRING_LEN];
  153. char *ptr, *ptr2;
  154. char search_STR[MAX_STRING_LEN];
  155. char replace_STR[MAX_STRING_LEN];
  156. if ((attr_vp = pairfind(request->config_items, PW_REWRITE_RULE)) != NULL){
  157. if (data->name == NULL || strcmp(data->name,attr_vp->vp_strvalue))
  158. return RLM_MODULE_NOOP;
  159. }
  160. if (data->new_attr){
  161. /* new_attribute = yes */
  162. if (!radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL)) {
  163. DEBUG2("%s: xlat on replace string failed.", data->name);
  164. return ret;
  165. }
  166. attr_vp = pairmake(data->attribute,replace_STR,0);
  167. if (attr_vp == NULL){
  168. DEBUG2("%s: Could not add new attribute %s with value '%s'", data->name,
  169. data->attribute,replace_STR);
  170. return ret;
  171. }
  172. switch(data->searchin){
  173. case RLM_REGEX_INPACKET:
  174. pairadd(&request->packet->vps,attr_vp);
  175. break;
  176. case RLM_REGEX_INCONFIG:
  177. pairadd(&request->config_items,attr_vp);
  178. break;
  179. case RLM_REGEX_INREPLY:
  180. pairadd(&request->reply->vps,attr_vp);
  181. break;
  182. case RLM_REGEX_INPROXY:
  183. if (!request->proxy) {
  184. pairbasicfree(attr_vp);
  185. return RLM_MODULE_NOOP;
  186. }
  187. pairadd(&request->proxy->vps, attr_vp);
  188. break;
  189. case RLM_REGEX_INPROXYREPLY:
  190. if (!request->proxy_reply) {
  191. pairbasicfree(attr_vp);
  192. return RLM_MODULE_NOOP;
  193. }
  194. pairadd(&request->proxy_reply->vps, attr_vp);
  195. break;
  196. default:
  197. radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name);
  198. data->searchin = RLM_REGEX_INPACKET;
  199. pairadd(&request->packet->vps,attr_vp);
  200. break;
  201. }
  202. DEBUG2("%s: Added attribute %s with value '%s'", data->name,data->attribute,replace_STR);
  203. ret = RLM_MODULE_OK;
  204. } else {
  205. int replace_len = 0;
  206. /* new_attribute = no */
  207. switch (data->searchin) {
  208. case RLM_REGEX_INPACKET:
  209. if (data->attr_num == PW_USER_NAME)
  210. attr_vp = request->username;
  211. else if (data->attr_num == PW_USER_PASSWORD)
  212. attr_vp = request->password;
  213. else
  214. tmp = request->packet->vps;
  215. break;
  216. case RLM_REGEX_INCONFIG:
  217. tmp = request->config_items;
  218. break;
  219. case RLM_REGEX_INREPLY:
  220. tmp = request->reply->vps;
  221. break;
  222. case RLM_REGEX_INPROXYREPLY:
  223. if (!request->proxy_reply)
  224. return RLM_MODULE_NOOP;
  225. tmp = request->proxy_reply->vps;
  226. break;
  227. case RLM_REGEX_INPROXY:
  228. if (!request->proxy)
  229. return RLM_MODULE_NOOP;
  230. tmp = request->proxy->vps;
  231. break;
  232. default:
  233. radlog(L_ERR, "%s: Illegal value for searchin. Changing to packet.", data->name);
  234. data->searchin = RLM_REGEX_INPACKET;
  235. attr_vp = pairfind(request->packet->vps, data->attr_num);
  236. break;
  237. }
  238. do_again:
  239. if (tmp != NULL)
  240. attr_vp = pairfind(tmp, data->attr_num);
  241. if (attr_vp == NULL) {
  242. DEBUG2("%s: Could not find value pair for attribute %s", data->name,data->attribute);
  243. return ret;
  244. }
  245. if (attr_vp->vp_strvalue == NULL || attr_vp->length == 0){
  246. DEBUG2("%s: Attribute %s string value NULL or of zero length", data->name,data->attribute);
  247. return ret;
  248. }
  249. cflags |= REG_EXTENDED;
  250. if (data->nocase)
  251. cflags |= REG_ICASE;
  252. if (!radius_xlat(search_STR, sizeof(search_STR), data->search, request, NULL) && data->search_len != 0) {
  253. DEBUG2("%s: xlat on search string failed.", data->name);
  254. return ret;
  255. }
  256. if ((err = regcomp(&preg,search_STR,cflags))) {
  257. regerror(err, &preg, err_msg, MAX_STRING_LEN);
  258. DEBUG2("%s: regcomp() returned error: %s", data->name,err_msg);
  259. return ret;
  260. }
  261. if ((attr_vp->type == PW_TYPE_IPADDR) &&
  262. (attr_vp->vp_strvalue[0] == '\0')) {
  263. inet_ntop(AF_INET, &(attr_vp->vp_ipaddr),
  264. attr_vp->vp_strvalue,
  265. sizeof(attr_vp->vp_strvalue));
  266. }
  267. ptr = new_str;
  268. ptr2 = attr_vp->vp_strvalue;
  269. counter = 0;
  270. for ( i = 0 ;i < (unsigned)data->num_matches; i++) {
  271. err = regexec(&preg, ptr2, REQUEST_MAX_REGEX, pmatch, 0);
  272. if (err == REG_NOMATCH) {
  273. if (i == 0) {
  274. DEBUG2("%s: Does not match: %s = %s", data->name,
  275. data->attribute, attr_vp->vp_strvalue);
  276. regfree(&preg);
  277. goto to_do_again;
  278. } else
  279. break;
  280. }
  281. if (err != 0) {
  282. regfree(&preg);
  283. radlog(L_ERR, "%s: match failure for attribute %s with value '%s'", data->name,
  284. data->attribute, attr_vp->vp_strvalue);
  285. return ret;
  286. }
  287. if (pmatch[0].rm_so == -1)
  288. break;
  289. len = pmatch[0].rm_so;
  290. if (data->append) {
  291. len = len + (pmatch[0].rm_eo - pmatch[0].rm_so);
  292. }
  293. counter += len;
  294. if (counter >= MAX_STRING_LEN) {
  295. regfree(&preg);
  296. DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
  297. data->attribute, attr_vp->vp_strvalue);
  298. return ret;
  299. }
  300. memcpy(ptr, ptr2,len);
  301. ptr += len;
  302. *ptr = '\0';
  303. ptr2 += pmatch[0].rm_eo;
  304. if (i == 0){
  305. /*
  306. * We only run on the first match, sorry
  307. */
  308. for(j = 0; j <= REQUEST_MAX_REGEX; j++){
  309. char *p;
  310. char buffer[sizeof(attr_vp->vp_strvalue)];
  311. /*
  312. * Stolen from src/main/valuepair.c, paircompare()
  313. */
  314. /*
  315. * Delete old matches if the corresponding match does not
  316. * exist in the current regex
  317. */
  318. if (pmatch[j].rm_so == -1){
  319. p = request_data_get(request,request,REQUEST_DATA_REGEX | j);
  320. if (p){
  321. free(p);
  322. continue;
  323. }
  324. break;
  325. }
  326. memcpy(buffer,
  327. attr_vp->vp_strvalue + pmatch[j].rm_so,
  328. pmatch[j].rm_eo - pmatch[j].rm_so);
  329. buffer[pmatch[j].rm_eo - pmatch[j].rm_so] = '\0';
  330. p = strdup(buffer);
  331. request_data_add(request,request,REQUEST_DATA_REGEX | j,p,free);
  332. }
  333. }
  334. if (!done_xlat){
  335. if (data->replace_len != 0 &&
  336. radius_xlat(replace_STR, sizeof(replace_STR), data->replace, request, NULL) == 0) {
  337. DEBUG2("%s: xlat on replace string failed.", data->name);
  338. return ret;
  339. }
  340. replace_len = (data->replace_len != 0) ? strlen(replace_STR) : 0;
  341. done_xlat = 1;
  342. }
  343. counter += replace_len;
  344. if (counter >= MAX_STRING_LEN) {
  345. regfree(&preg);
  346. DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
  347. data->attribute, attr_vp->vp_strvalue);
  348. return ret;
  349. }
  350. if (replace_len){
  351. memcpy(ptr, replace_STR, replace_len);
  352. ptr += replace_len;
  353. *ptr = '\0';
  354. }
  355. }
  356. regfree(&preg);
  357. len = strlen(ptr2) + 1; /* We add the ending NULL */
  358. counter += len;
  359. if (counter >= MAX_STRING_LEN){
  360. DEBUG2("%s: Replacement out of limits for attribute %s with value '%s'", data->name,
  361. data->attribute, attr_vp->vp_strvalue);
  362. return ret;
  363. }
  364. memcpy(ptr, ptr2, len);
  365. ptr[len] = '\0';
  366. DEBUG2("%s: Changed value for attribute %s from '%s' to '%s'", data->name,
  367. data->attribute, attr_vp->vp_strvalue, new_str);
  368. if (pairparsevalue(attr_vp, new_str) == NULL) {
  369. DEBUG2("%s: Could not write value '%s' into attribute %s: %s", data->name, new_str, data->attribute, fr_strerror());
  370. return ret;
  371. }
  372. to_do_again:
  373. ret = RLM_MODULE_OK;
  374. if (tmp != NULL){
  375. tmp = attr_vp->next;
  376. if (tmp != NULL)
  377. goto do_again;
  378. }
  379. }
  380. return ret;
  381. }
  382. static int attr_rewrite_accounting(void *instance, REQUEST *request)
  383. {
  384. return do_attr_rewrite(instance, request);
  385. }
  386. static int attr_rewrite_authorize(void *instance, REQUEST *request)
  387. {
  388. return do_attr_rewrite(instance, request);
  389. }
  390. static int attr_rewrite_authenticate(void *instance, REQUEST *request)
  391. {
  392. return do_attr_rewrite(instance, request);
  393. }
  394. static int attr_rewrite_preacct(void *instance, REQUEST *request)
  395. {
  396. return do_attr_rewrite(instance, request);
  397. }
  398. static int attr_rewrite_checksimul(void *instance, REQUEST *request)
  399. {
  400. return do_attr_rewrite(instance, request);
  401. }
  402. static int attr_rewrite_preproxy(void *instance, REQUEST *request)
  403. {
  404. return do_attr_rewrite(instance, request);
  405. }
  406. static int attr_rewrite_postproxy(void *instance, REQUEST *request)
  407. {
  408. return do_attr_rewrite(instance, request);
  409. }
  410. static int attr_rewrite_postauth(void *instance, REQUEST *request)
  411. {
  412. return do_attr_rewrite(instance, request);
  413. }
  414. static int attr_rewrite_detach(void *instance)
  415. {
  416. free(instance);
  417. return 0;
  418. }
  419. /*
  420. * The module name should be the only globally exported symbol.
  421. * That is, everything else should be 'static'.
  422. *
  423. * If the module needs to temporarily modify it's instantiation
  424. * data, the type should be changed to RLM_TYPE_THREAD_UNSAFE.
  425. * The server will then take care of ensuring that the module
  426. * is single-threaded.
  427. */
  428. module_t rlm_attr_rewrite = {
  429. RLM_MODULE_INIT,
  430. "attr_rewrite",
  431. RLM_TYPE_THREAD_UNSAFE, /* type */
  432. attr_rewrite_instantiate, /* instantiation */
  433. attr_rewrite_detach, /* detach */
  434. {
  435. attr_rewrite_authenticate, /* authentication */
  436. attr_rewrite_authorize, /* authorization */
  437. attr_rewrite_preacct, /* preaccounting */
  438. attr_rewrite_accounting, /* accounting */
  439. attr_rewrite_checksimul, /* checksimul */
  440. attr_rewrite_preproxy, /* pre-proxy */
  441. attr_rewrite_postproxy, /* post-proxy */
  442. attr_rewrite_postauth /* post-auth */
  443. },
  444. };