⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 mod_cas.c

📁 cas 客户端文件
💻 C
📖 第 1 页 / 共 3 页
字号:
/* * "Require NetID Authentication" module that allows a server to act as a * client of the Central Authentication Service. * * Original author: Shawn Bayern <shawn.bayern@yale.edu> * Original version: February, 2001 * * Current maintainer: Drew Mazurek <drew.mazurek@yale.edu> * * Note: the "LOG" messages are entirely for debugging and should not be * enabled for any production deployment.  They're somewhat arbitrary; * we've found them useful at Yale for debugging new features, so I've * left them in in case they're useful. */#include "httpd.h"#include "http_config.h"#include "http_core.h"#include "http_log.h"#include "http_main.h"#include "http_protocol.h"#include "util_script.h"#include "apr_strings.h"#include "http_connection.h"#include "ap_config.h"#include <sys/file.h>#include <sys/mman.h>#include <sys/stat.h>#include <sys/types.h>#include <unistd.h>#include <fcntl.h>#include <stdio.h>#include <grp.h>#include <pwd.h>#include "cas.h"#include "ticketcache.h"#define URLBUFSIZE 4096#define USERBUFSIZE 20#define TICKETBUFSIZE 60// support two different cookie names to allow secure and insecure cookies// to exist side-by-side#define COOKIENAME "MODCASID"#define COOKIENAME_SECURE "MODCASIDS"#define COOKIEPATH "/"#define DEFAULT_CASLocalCacheFile NULL#define DEFAULT_CASLocalCacheSize 1000#define DEFAULT_CASLocalCacheTimeout 3600#define DEFAULT_CASLocalCacheInsecure 0#define DEFAULT_CASLogoutParameter NULL#define DEFAULT_CASLogoutLocalURL NULL//#define DEBUG#undef DEBUG#ifdef DEBUG# define LOG(X) log(X)#else# define LOG(X) #endif/* Utility function declarations */static char *get_service(request_rec *r, char *buf, int buflen);static char *get_ticket(request_rec *r);static char *get_ticket_from_cookies(request_rec *r);static void log(const char *msg);static void logout(request_rec *r);static void cache_invalidate(char *ticket);static int is_user_in_group(const char *user, const char *group, apr_pool_t *p);static int is_parameter_true(request_rec *r, char *param);static void write_lock(int fd);static void read_lock(int fd);static void un_lock(int fd);/* Our exported link to Apache. */module AP_MODULE_DECLARE_DATA cas_module;/* our "configuration record" */typedef struct {   char *CASLocalCacheFile;   char *CASLogoutParameter;   char *CASLogoutLocalURL;   char *CASEGDFile;   int CASLocalCacheSize;   time_t CASLocalCacheTimeout;   char CASLocalCacheInsecure;} mod_cas_conf;/* support functions that we want to prototype here */static int check_individual_cookie(void *netid, char *key, char *value);static int check_individual_cookie_for_ticket(void *netid, char *key, char *value);static char *get_netid_from_cookies(request_rec *r);static void create_and_send_new_ticket(request_rec *r);/* effectively per-process local pointer to our mmap()'d ticket cache */static char *ticket_cache = NULL;static size_t ticket_cache_size = 0;static int ticket_cache_fd;/* * Reads CASLocalCacheFile from the configuration file. */static const char *read_CASLocalCacheFile(	cmd_parms *cmd, void *dummy, const char *word) {    mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config(	cmd->server->module_config, &cas_module);    c->CASLocalCacheFile = (char *) word;    return NULL;}/* * Reads CASLogoutParameter from the configuration file. */static const char *read_CASLogoutParameter(	cmd_parms *cmd, void *dummy, const char *word) {    mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config(	cmd->server->module_config, &cas_module);    c->CASLogoutParameter = (char *) word;    return NULL;}/* * Reads CASLogoutLocalURL from the configuration file. */static const char *read_CASLogoutLocalURL(	cmd_parms *cmd, void *dummy, const char *word) {    mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config(	cmd->server->module_config, &cas_module);    c->CASLogoutLocalURL = (char *) word;    return NULL;}/* * Reads CASEGDFile from the configuration file. */static const char *read_CASEGDFile(	cmd_parms *cmd, void *dummy, const char *word) {    mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config(	cmd->server->module_config, &cas_module);    c->CASEGDFile = (char *) word;    return NULL;}/* * Reads CASLocalCacheSize from the configuration file. */static const char *read_CASLocalCacheSize(	cmd_parms *cmd, void *dummy, const char *word) {    mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config(	cmd->server->module_config, &cas_module);    c->CASLocalCacheSize = atoi(word);    if (c->CASLocalCacheSize <= 0)	return "CASLocalCacheSize must be a positive number";    return NULL;}/* * Reads CASLocalCacheTimeout from the configuration file. */static const char *read_CASLocalCacheTimeout(	cmd_parms *cmd, void *dummy, const char *word) {    mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config(	cmd->server->module_config, &cas_module);    c->CASLocalCacheTimeout = atoi(word);    if (c->CASLocalCacheTimeout <= 0)	return "CASLocalCacheTimeout must be a positive number";    return NULL;}/* * Reads CASLocalCacheInsecure from the configuration file. */static const char *read_CASLocalCacheInsecure(	cmd_parms *cmd, void *dummy, int bool) {    mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config(	cmd->server->module_config, &cas_module);    c->CASLocalCacheInsecure = (!(!bool));    return NULL;}/* * Returns a newly allocated server configuration record, filled in with the * default values.  Since we provide no "merge" routine, we expect that * more specific settings will occlude less specific settings.  This may * be important -- for instance, if there is a virtual server configured * for SSL support on a server that is otherwise unprotected. */static void *init_server_config(apr_pool_t *p, server_rec *s) {    mod_cas_conf *c = apr_pcalloc(p, sizeof(mod_cas_conf));    c->CASLocalCacheFile = DEFAULT_CASLocalCacheFile;    c->CASLocalCacheSize = DEFAULT_CASLocalCacheSize;    c->CASLocalCacheTimeout = DEFAULT_CASLocalCacheTimeout;    c->CASLocalCacheInsecure = DEFAULT_CASLocalCacheInsecure;    c->CASLogoutParameter = DEFAULT_CASLogoutParameter;    c->CASLogoutLocalURL = DEFAULT_CASLogoutLocalURL;    return c;}/* * Called upon the exit of a child process.  mod_cas currently takes this * opportunity to munmap() our cache database if one was previously * mmap()'d. */static apr_status_t cleanup_child(void *server_rec) {    if (ticket_cache)	munmap(ticket_cache, ticket_cache_size);    return APR_SUCCESS;}/* * Called upon the initializaion of a child process.  mod_cas currently * takes this opportunity to mmap() our cache database if one has been * requested. */static void init_child(apr_pool_t *p, server_rec *s) {    int fd;    mod_cas_conf *c;    apr_pool_cleanup_register(p, s, cleanup_child, cleanup_child);    LOG("in init_child()\n");    // if ticket_cache is already initialized, don't bother re-mapping it    if (ticket_cache)	return;    // retrieve our configuration    c = (mod_cas_conf *) ap_get_module_config(s->module_config, &cas_module);    if (c != NULL)         LOG("-> c is not null\n");    else        LOG("-> c is null\n");    // don't do anything if the server admin doesn't want a cache    if (!(c->CASLocalCacheFile))	return;    LOG("-> dereferenced c\n");    // if a cache file is requested, mmap() it and store its in-memory    // location in our static, per-process cell    fd = open(c->CASLocalCacheFile, O_RDWR | O_CREAT, 0600);    if (fd < 0) {	LOG("-> couldn't open CAS cache file\n");	return;					// open() failure    }    // make sure the file is as large as we need it to be    ticket_cache_size =	sizeof(int) + (c->CASLocalCacheSize * sizeof(TicketEntry));    lseek(fd, ticket_cache_size, SEEK_SET);    write(fd, "", 1);    ticket_cache =	mmap(0, ticket_cache_size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);    /*      * If mmap() failed, there's really not much we can do other than     * abandon the idea of a ticket cache, at least for us.  But at least     * we can set ticket_cache back to NULL as a marker.     */    if (ticket_cache == MAP_FAILED) {	ticket_cache = NULL;	LOG("-> mmap() failed\n");	return;    }    ticket_cache_fd = fd;    LOG("-> init_child() done\n");}/* * Handles CAS authentication.  Redirects to CAS if we can't authenticate * the user, and expects a ticket back.  Sets r->user with * a validated user.  Operates only when the AuthType is set to "CAS." * * Once past this point, we never directly return DECLINED; we return * success with OK and, if we can't authenticate the user, merely redirect * to the CAS with HTTP_MOVED_TEMPORARILY.  On error, we return * HTTP_INTERNAL_SERVER_ERROR.  If we return OK, no other modules are * supposed to handle this request during the "check user" phase. */static int do_cas(request_rec *r){    // it's okay to use these buffers since we're single-threaded    char *urlbuf = (char *) apr_pcalloc(r->pool, URLBUFSIZE);    char *userbuf = (char *) apr_pcalloc(r->connection->pool, USERBUFSIZE);    char *service, *ticket;    const char *auth_type = ap_auth_type(r);    char *p;    int validate_status = 0;    LOG("in do_cas()\n");    /* We only have something to do if "CAS" is the AuthType. */    if (auth_type && strcmp(auth_type, "CAS"))      return DECLINED;    /*     * Okay, we're relevant.  Now, if we've been handed a cookie that     * identifies the user (with the help of the ticket cache), we can     * simply honor the user's preauthenticated status.  (This call     * uses the ticket cache behind the scenes.  Separately, as a nice     * side effect, this sets r->user to NULL if we don't     * find a cookie; that way, if we proceed, we know that r->connnection->     * user doesn't have some bogus value in in.     */    if (r->user = get_netid_from_cookies(r)) {        mod_cas_conf *c;        // retrieve our configuration        c = (mod_cas_conf *) ap_get_module_config(	    r->server->module_config, &cas_module);	if (c->CASLogoutParameter && c->CASLogoutLocalURL 		&& is_parameter_true(r,c->CASLogoutParameter)) {	    LOG("-> logout parameter set... logging user out\n");	    logout(r);	    LOG("-> redirecting to logout page\n");	    apr_table_set(r->headers_out, "Location", c->CASLogoutLocalURL);	    return HTTP_MOVED_TEMPORARILY;	} else {	    LOG("-> authenticated from cookie\n");	    return OK;	}    }    /*     * "CAS" is the AuthType, so we consider the request in terms of     * a service and a ticket.     */    service = get_service(r, urlbuf, URLBUFSIZE);    ticket = get_ticket(r);    LOG("  service = '"); LOG(service);    LOG("'; ticket = '");    if (ticket)        LOG(ticket);    else        LOG("(null)");    LOG("'\n");    if (service == NULL) {      ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "mod_cas: null "		   "service");      return HTTP_INTERNAL_SERVER_ERROR;	/* shouldn't happen */    }    LOG("about to call CAS_validate()\n");    if (ticket != NULL        && (validate_status = CAS_validate(ticket, service, userbuf, USERBUFSIZE)) == 1) {      LOG("-> successful primary authentication for '");      LOG(userbuf);      LOG("'\n");      // Successful authentication      r->user = userbuf;      // If relevant, send a cookie since we authenticated the user with      // a real CAS ticket      create_and_send_new_ticket(r);      // Remove ticket from query string      p = r->args;      if (p != NULL)	if (p == strstr(p, "ticket="))			// a comparison...	  *p = '\0';	else if (p = strstr(p, "&ticket="))		// yes, we're setting p	  *p = '\0';      return OK;    } else if (validate_status == BAD_CERT) {      LOG("-> certificate error\n");      ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "mod_cas: certificate "		"error: bad cert or verisignserverca.pem is not available.");      return HTTP_INTERNAL_SERVER_ERROR;    } else {      /* Bad or missing ticket, so bounce the user to CAS. */      char *temp = alloca(strlen(CAS_LOGIN_URL) + strlen("?service=")          + strlen(service) + 1);      if (!temp) {	ap_log_error(APLOG_MARK, APLOG_ERR, 0, r->server, "mod_cas: alloca "		     "failed in function create_and_send_new_ticket()");	return HTTP_INTERNAL_SERVER_ERROR;      }      strcpy(temp, CAS_LOGIN_URL);      strcat(temp, "?service=");      strcat(temp, service);      apr_table_set(r->headers_out, "Location", temp);      return HTTP_MOVED_TEMPORARILY;    }}/* * Compares the authenticated user (from CAS) with the list of users (and * groups) authorized by the "Require" directives.  NOTE: We implement * set union here because that's how Apache modules typically work; the word * "Require" might incorrectly lead you to assume that we perform set

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -