Advertisement
Guest User

Project Pass

a guest
Mar 21st, 2018
116
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 15.90 KB | None | 0 0
  1. #include "apr_strings.h"
  2. #include "apr_md5.h" /* for apr_password_validate */
  3.  
  4. #include "ap_config.h"
  5. #include "ap_provider.h"
  6. #include "httpd.h"
  7. #include "http_config.h"
  8. #include "http_core.h"
  9. #include "http_log.h"
  10. #include "http_protocol.h"
  11. #include "http_request.h"
  12.  
  13. #include "mod_auth.h"
  14.  
  15. #include "ap_socache.h"
  16. #include "util_mutex.h"
  17. #include "apr_optional.h"
  18.  
  19. module AP_MODULE_DECLARE_DATA authn_socache_module;
  20.  
  21. typedef struct authn_cache_dircfg {
  22. apr_interval_time_t timeout;
  23. apr_array_header_t *providers;
  24. const char *context;
  25. } authn_cache_dircfg;
  26.  
  27. /* FIXME: figure out usage of socache create vs init
  28. * I think the cache and mutex should be global
  29. */
  30. static apr_global_mutex_t *authn_cache_mutex = NULL;
  31. static ap_socache_provider_t *socache_provider = NULL;
  32. static ap_socache_instance_t *socache_instance = NULL;
  33. static const char *const authn_cache_id = "authn-socache";
  34. static int configured;
  35.  
  36. static apr_status_t remove_lock(void *data)
  37. {
  38. if (authn_cache_mutex) {
  39. apr_global_mutex_destroy(authn_cache_mutex);
  40. authn_cache_mutex = NULL;
  41. }
  42. return APR_SUCCESS;
  43. }
  44. static apr_status_t destroy_cache(void *data)
  45. {
  46. if (socache_instance) {
  47. socache_provider->destroy(socache_instance, (server_rec*)data);
  48. socache_instance = NULL;
  49. }
  50. return APR_SUCCESS;
  51. }
  52.  
  53.  
  54. static int authn_cache_precfg(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptmp)
  55. {
  56. apr_status_t rv = ap_mutex_register(pconf, authn_cache_id,
  57. NULL, APR_LOCK_DEFAULT, 0);
  58. if (rv != APR_SUCCESS) {
  59. ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01673)
  60. "failed to register %s mutex", authn_cache_id);
  61. return 500; /* An HTTP status would be a misnomer! */
  62. }
  63. socache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP,
  64. AP_SOCACHE_DEFAULT_PROVIDER,
  65. AP_SOCACHE_PROVIDER_VERSION);
  66. configured = 0;
  67. return OK;
  68. }
  69. static int authn_cache_post_config(apr_pool_t *pconf, apr_pool_t *plog,
  70. apr_pool_t *ptmp, server_rec *s)
  71. {
  72. apr_status_t rv;
  73. const char *errmsg;
  74. static struct ap_socache_hints authn_cache_hints = {64, 32, 60000000};
  75.  
  76. if (!configured) {
  77. return OK; /* don't waste the overhead of creating mutex & cache */
  78. }
  79. if (socache_provider == NULL) {
  80. ap_log_perror(APLOG_MARK, APLOG_CRIT, 0, plog, APLOGNO(01674)
  81. "Please select a socache provider with AuthnCacheSOCache "
  82. "(no default found on this platform). Maybe you need to "
  83. "load mod_socache_shmcb or another socache module first");
  84. return 500; /* An HTTP status would be a misnomer! */
  85. }
  86.  
  87. rv = ap_global_mutex_create(&authn_cache_mutex, NULL,
  88. authn_cache_id, NULL, s, pconf, 0);
  89. if (rv != APR_SUCCESS) {
  90. ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01675)
  91. "failed to create %s mutex", authn_cache_id);
  92. return 500; /* An HTTP status would be a misnomer! */
  93. }
  94. apr_pool_cleanup_register(pconf, NULL, remove_lock, apr_pool_cleanup_null);
  95.  
  96. errmsg = socache_provider->create(&socache_instance, NULL, ptmp, pconf);
  97. if (errmsg) {
  98. ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01676) "%s", errmsg);
  99. return 500; /* An HTTP status would be a misnomer! */
  100. }
  101.  
  102. rv = socache_provider->init(socache_instance, authn_cache_id,
  103. &authn_cache_hints, s, pconf);
  104. if (rv != APR_SUCCESS) {
  105. ap_log_perror(APLOG_MARK, APLOG_CRIT, rv, plog, APLOGNO(01677)
  106. "failed to initialise %s cache", authn_cache_id);
  107. return 500; /* An HTTP status would be a misnomer! */
  108. }
  109. apr_pool_cleanup_register(pconf, (void*)s, destroy_cache, apr_pool_cleanup_null);
  110. return OK;
  111. }
  112. static void authn_cache_child_init(apr_pool_t *p, server_rec *s)
  113. {
  114. const char *lock;
  115. apr_status_t rv;
  116. if (!configured) {
  117. return; /* don't waste the overhead of creating mutex & cache */
  118. }
  119. lock = apr_global_mutex_lockfile(authn_cache_mutex);
  120. rv = apr_global_mutex_child_init(&authn_cache_mutex, lock, p);
  121. if (rv != APR_SUCCESS) {
  122. ap_log_error(APLOG_MARK, APLOG_CRIT, rv, s, APLOGNO(01678)
  123. "failed to initialise mutex in child_init");
  124. }
  125. }
  126.  
  127. static const char *authn_cache_socache(cmd_parms *cmd, void *CFG,
  128. const char *arg)
  129. {
  130. const char *errmsg = ap_check_cmd_context(cmd, GLOBAL_ONLY);
  131. if (errmsg)
  132. return errmsg;
  133. socache_provider = ap_lookup_provider(AP_SOCACHE_PROVIDER_GROUP, arg,
  134. AP_SOCACHE_PROVIDER_VERSION);
  135. if (socache_provider == NULL) {
  136. errmsg = apr_psprintf(cmd->pool,
  137. "Unknown socache provider '%s'. Maybe you need "
  138. "to load the appropriate socache module "
  139. "(mod_socache_%s?)", arg, arg);
  140. }
  141. return errmsg;
  142. }
  143.  
  144. static const char *authn_cache_enable(cmd_parms *cmd, void *CFG)
  145. {
  146. const char *errmsg = ap_check_cmd_context(cmd, GLOBAL_ONLY);
  147. configured = 1;
  148. return errmsg;
  149. }
  150.  
  151. static const char *const directory = "directory";
  152. static void* authn_cache_dircfg_create(apr_pool_t *pool, char *s)
  153. {
  154. authn_cache_dircfg *ret = apr_palloc(pool, sizeof(authn_cache_dircfg));
  155. ret->timeout = apr_time_from_sec(300);
  156. ret->providers = NULL;
  157. ret->context = directory;
  158. return ret;
  159. }
  160. /* not sure we want this. Might be safer to document use-all-or-none */
  161. static void* authn_cache_dircfg_merge(apr_pool_t *pool, void *BASE, void *ADD)
  162. {
  163. authn_cache_dircfg *base = BASE;
  164. authn_cache_dircfg *add = ADD;
  165. authn_cache_dircfg *ret = apr_pmemdup(pool, add, sizeof(authn_cache_dircfg));
  166. /* preserve context and timeout if not defaults */
  167. if (add->context == directory) {
  168. ret->context = base->context;
  169. }
  170. if (add->timeout == apr_time_from_sec(300)) {
  171. ret->timeout = base->timeout;
  172. }
  173. if (add->providers == NULL) {
  174. ret->providers = base->providers;
  175. }
  176. return ret;
  177. }
  178.  
  179. static const char *authn_cache_setprovider(cmd_parms *cmd, void *CFG,
  180. const char *arg)
  181. {
  182. authn_cache_dircfg *cfg = CFG;
  183. if (cfg->providers == NULL) {
  184. cfg->providers = apr_array_make(cmd->pool, 4, sizeof(const char*));
  185. }
  186. APR_ARRAY_PUSH(cfg->providers, const char*) = arg;
  187. configured = 1;
  188. return NULL;
  189. }
  190.  
  191. static const char *authn_cache_timeout(cmd_parms *cmd, void *CFG,
  192. const char *arg)
  193. {
  194. authn_cache_dircfg *cfg = CFG;
  195. int secs = atoi(arg);
  196. cfg->timeout = apr_time_from_sec(secs);
  197. return NULL;
  198. }
  199.  
  200. static const command_rec authn_cache_cmds[] =
  201. {
  202. /* global stuff: cache and mutex */
  203. AP_INIT_TAKE1("AuthnCacheSOCache", authn_cache_socache, NULL, RSRC_CONF,
  204. "socache provider for authn cache"),
  205. AP_INIT_NO_ARGS("AuthnCacheEnable", authn_cache_enable, NULL, RSRC_CONF,
  206. "enable socache configuration in htaccess even if not enabled anywhere else"),
  207. /* per-dir stuff */
  208. AP_INIT_ITERATE("AuthnCacheProvideFor", authn_cache_setprovider, NULL,
  209. OR_AUTHCFG, "Determine what authn providers to cache for"),
  210. AP_INIT_TAKE1("AuthnCacheTimeout", authn_cache_timeout, NULL,
  211. OR_AUTHCFG, "Timeout (secs) for cached credentials"),
  212. AP_INIT_TAKE1("AuthnCacheContext", ap_set_string_slot,
  213. (void*)APR_OFFSETOF(authn_cache_dircfg, context),
  214. ACCESS_CONF, "Context for authn cache"),
  215. {NULL}
  216. };
  217.  
  218. static const char *construct_key(request_rec *r, const char *context,
  219. const char *user, const char *realm)
  220. {
  221. /* handle "special" context values */
  222. if (!strcmp(context, "directory")) {
  223. /* FIXME: are we at risk of this blowing up? */
  224. char *new_context;
  225. char *slash = strrchr(r->uri, '/');
  226. new_context = apr_palloc(r->pool, slash - r->uri +
  227. strlen(r->server->server_hostname) + 1);
  228. strcpy(new_context, r->server->server_hostname);
  229. strncat(new_context, r->uri, slash - r->uri);
  230. context = new_context;
  231. }
  232. else if (!strcmp(context, "server")) {
  233. context = r->server->server_hostname;
  234. }
  235. /* any other context value is literal */
  236.  
  237. if (realm == NULL) { /* basic auth */
  238. return apr_pstrcat(r->pool, context, ":", user, NULL);
  239. }
  240. else { /* digest auth */
  241. return apr_pstrcat(r->pool, context, ":", user, ":", realm, NULL);
  242. }
  243. }
  244. static void ap_authn_cache_store(request_rec *r, const char *module,
  245. const char *user, const char *realm,
  246. const char* data)
  247. {
  248. apr_status_t rv;
  249. authn_cache_dircfg *dcfg;
  250. const char *key;
  251. apr_time_t expiry;
  252. int i;
  253. int use_cache = 0;
  254.  
  255. /* first check whether we're cacheing for this module */
  256. dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
  257. if (!configured || !dcfg->providers) {
  258. return;
  259. }
  260. for (i = 0; i < dcfg->providers->nelts; ++i) {
  261. if (!strcmp(module, APR_ARRAY_IDX(dcfg->providers, i, const char*))) {
  262. use_cache = 1;
  263. break;
  264. }
  265. }
  266. if (!use_cache) {
  267. return;
  268. }
  269.  
  270. /* OK, we're on. Grab mutex to do our business */
  271. rv = apr_global_mutex_trylock(authn_cache_mutex);
  272. if (APR_STATUS_IS_EBUSY(rv)) {
  273. /* don't wait around; just abandon it */
  274. ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, APLOGNO(01679)
  275. "authn credentials for %s not cached (mutex busy)", user);
  276. return;
  277. }
  278. else if (rv != APR_SUCCESS) {
  279. ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01680)
  280. "Failed to cache authn credentials for %s in %s",
  281. module, dcfg->context);
  282. return;
  283. }
  284.  
  285. /* We have the mutex, so go ahead */
  286. /* first build our key and determine expiry time */
  287. key = construct_key(r, dcfg->context, user, realm);
  288. expiry = apr_time_now() + dcfg->timeout;
  289.  
  290. /* store it */
  291. rv = socache_provider->store(socache_instance, r->server,
  292. (unsigned char*)key, strlen(key), expiry,
  293. (unsigned char*)data, strlen(data), r->pool);
  294. if (rv == APR_SUCCESS) {
  295. ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01681)
  296. "Cached authn credentials for %s in %s",
  297. user, dcfg->context);
  298. }
  299. else {
  300. ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01682)
  301. "Failed to cache authn credentials for %s in %s",
  302. module, dcfg->context);
  303. }
  304.  
  305. /* We're done with the mutex */
  306. rv = apr_global_mutex_unlock(authn_cache_mutex);
  307. if (rv != APR_SUCCESS) {
  308. ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01683) "Failed to release mutex!");
  309. }
  310. return;
  311. }
  312.  
  313. #define MAX_VAL_LEN 100
  314. static authn_status check_password(request_rec *r, const char *user,
  315. const char *password)
  316. {
  317.  
  318. /* construct key
  319. * look it up
  320. * if found, test password
  321. *
  322. * mutexing here would be a big performance drag.
  323. * It's definitely unnecessary with some backends (like ndbm or gdbm)
  324. * Is there a risk in the general case? I guess the only risk we
  325. * care about is a race condition that gets us a dangling pointer
  326. * to no-longer-defined memory. Hmmm ...
  327. */
  328. apr_status_t rv;
  329. const char *key;
  330. authn_cache_dircfg *dcfg;
  331. unsigned char val[MAX_VAL_LEN];
  332. unsigned int vallen = MAX_VAL_LEN - 1;
  333. dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
  334. if (!configured || !dcfg->providers) {
  335. return AUTH_USER_NOT_FOUND;
  336. }
  337. key = construct_key(r, dcfg->context, user, NULL);
  338. rv = socache_provider->retrieve(socache_instance, r->server,
  339. (unsigned char*)key, strlen(key),
  340. val, &vallen, r->pool);
  341.  
  342. if (APR_STATUS_IS_NOTFOUND(rv)) {
  343. /* not found - just return */
  344. ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01684)
  345. "Authn cache: no credentials found for %s", user);
  346. return AUTH_USER_NOT_FOUND;
  347. }
  348. else if (rv == APR_SUCCESS) {
  349. /* OK, we got a value */
  350. ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01685)
  351. "Authn cache: found credentials for %s", user);
  352. val[vallen] = 0;
  353. }
  354. else {
  355. /* error: give up and pass the buck */
  356. /* FIXME: getting this for NOTFOUND - prolly a bug in mod_socache */
  357. ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01686)
  358. "Error accessing authentication cache");
  359. return AUTH_USER_NOT_FOUND;
  360. }
  361.  
  362. rv = apr_password_validate(password, (char*) val);
  363. if (rv != APR_SUCCESS) {
  364. return AUTH_DENIED;
  365. }
  366.  
  367. return AUTH_GRANTED;
  368. }
  369.  
  370. static authn_status get_realm_hash(request_rec *r, const char *user,
  371. const char *realm, char **rethash)
  372. {
  373. apr_status_t rv;
  374. const char *key;
  375. authn_cache_dircfg *dcfg;
  376. unsigned char val[MAX_VAL_LEN];
  377. unsigned int vallen = MAX_VAL_LEN - 1;
  378. dcfg = ap_get_module_config(r->per_dir_config, &authn_socache_module);
  379. if (!configured || !dcfg->providers) {
  380. return AUTH_USER_NOT_FOUND;
  381. }
  382. key = construct_key(r, dcfg->context, user, realm);
  383. rv = socache_provider->retrieve(socache_instance, r->server,
  384. (unsigned char*)key, strlen(key),
  385. val, &vallen, r->pool);
  386.  
  387. if (APR_STATUS_IS_NOTFOUND(rv)) {
  388. /* not found - just return */
  389. ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01687)
  390. "Authn cache: no credentials found for %s", user);
  391. return AUTH_USER_NOT_FOUND;
  392. }
  393. else if (rv == APR_SUCCESS) {
  394. /* OK, we got a value */
  395. ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01688)
  396. "Authn cache: found credentials for %s", user);
  397. }
  398. else {
  399. /* error: give up and pass the buck */
  400. /* FIXME: getting this for NOTFOUND - prolly a bug in mod_socache */
  401. ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r, APLOGNO(01689)
  402. "Error accessing authentication cache");
  403. return AUTH_USER_NOT_FOUND;
  404. }
  405. *rethash = apr_pstrmemdup(r->pool, (char *)val, vallen);
  406.  
  407. return AUTH_USER_FOUND;
  408. }
  409.  
  410. static const authn_provider authn_cache_provider =
  411. {
  412. &check_password,
  413. &get_realm_hash,
  414. };
  415. static void register_hooks(apr_pool_t *p)
  416. {
  417. ap_register_auth_provider(p, AUTHN_PROVIDER_GROUP, "socache",
  418. AUTHN_PROVIDER_VERSION,
  419. &authn_cache_provider, AP_AUTH_INTERNAL_PER_CONF);
  420. APR_REGISTER_OPTIONAL_FN(ap_authn_cache_store);
  421. ap_hook_pre_config(authn_cache_precfg, NULL, NULL, APR_HOOK_MIDDLE);
  422. ap_hook_post_config(authn_cache_post_config, NULL, NULL, APR_HOOK_MIDDLE);
  423. ap_hook_child_init(authn_cache_child_init, NULL, NULL, APR_HOOK_MIDDLE);
  424. }
  425.  
  426. AP_DECLARE_MODULE(authn_socache) =
  427. {
  428. STANDARD20_MODULE_STUFF,
  429. authn_cache_dircfg_create,
  430. authn_cache_dircfg_merge,
  431. NULL,
  432. NULL,
  433. authn_cache_cmds,
  434. register_hooks
  435. };
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement