📄 mod_cas.c
字号:
/* * Copyright (c) 2000-2003 Yale University. All rights reserved. * * THIS SOFTWARE IS PROVIDED "AS IS," AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE, ARE EXPRESSLY * DISCLAIMED. IN NO EVENT SHALL YALE UNIVERSITY OR ITS EMPLOYEES BE * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED, THE COSTS OF * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED IN ADVANCE OF THE POSSIBILITY OF SUCH * DAMAGE. * * Redistribution and use of this software in source or binary forms, * with or without modification, are permitted, provided that the * following conditions are met: * * 1. Any redistribution must include the above copyright notice and * disclaimer and this list of conditions in any related documentation * and, if feasible, in the redistributed software. * * 2. Any redistribution must include the acknowledgment, "This product * includes software developed by Yale University," in any related * documentation and, if feasible, in the redistributed software. * * 3. The names "Yale" and "Yale University" must not be used to endorse * or promote products derived from this software. *//* * "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 <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 "cas.h"#include "ticketcache.h"#define URLBUFSIZE 4096#define USERBUFSIZE 20// 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_CASEGDFile NULL#define DEFAULT_CASLocalCacheSize 1000#define DEFAULT_CASLocalCacheTimeout 3600#define DEFAULT_CASLocalCacheInsecure 0//#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 void log(const char *msg);static int is_user_in_group(const char *user, const char *group, pool *p);static int check_individual_cookie(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);static void write_lock(int fd);static void read_lock(int fd);static void un_lock(int fd);/* Our exported link to Apache. */module MODULE_VAR_EXPORT cas_module;/* our internal "configuration record" */typedef struct { char *CASLocalCacheFile; char *CASEGDFile; int CASLocalCacheSize; time_t CASLocalCacheTimeout; char CASLocalCacheInsecure;} mod_cas_conf;/* 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, char *word) { mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config( cmd->server->module_config, &cas_module); c->CASLocalCacheFile = word; return NULL;}/* * Reads CASLocalCacheSize from the configuration file. */static const char *read_CASLocalCacheSize( cmd_parms *cmd, void *dummy, 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, 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 CASEGDFile from the configuration file. */static const char *read_CASEGDFile( cmd_parms *cmd, void *dummy, char *word) { mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config( cmd->server->module_config, &cas_module); c->CASEGDFile = word; 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(pool *p, server_rec *s) { mod_cas_conf *c = ap_pcalloc(p, sizeof(mod_cas_conf)); c->CASLocalCacheFile = DEFAULT_CASLocalCacheFile; c->CASLocalCacheSize = DEFAULT_CASLocalCacheSize; c->CASLocalCacheTimeout = DEFAULT_CASLocalCacheTimeout; c->CASLocalCacheInsecure = DEFAULT_CASLocalCacheInsecure; c->CASEGDFile = DEFAULT_CASEGDFile; return c;}/* * 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(server_rec *s, pool *p) { int fd; mod_cas_conf *c; 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");}/* * 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 void cleanup_child(server_rec *s, pool *p) { if (ticket_cache) munmap(ticket_cache, ticket_cache_size);}/* * Handles CAS authentication. Redirects to CAS if we can't authenticate * the user, and expects a ticket back. Sets r->connection->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 *) ap_pcalloc(r->pool, URLBUFSIZE); char *userbuf = (char *) ap_pcalloc(r->connection->pool, USERBUFSIZE); char *service, *ticket; const char *auth_type = ap_auth_type(r); char *p; int validate_status = 0; /* 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->connection->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->connection->user = get_netid_from_cookies(r)) 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_rerror(APLOG_MARK, APLOG_ERR, r, "mod_cas: null service\n"); 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->connection->user = userbuf; // If relevant, send a cookie since we authenticated the user with // a real CAS ticket create_and_send_new_ticket(r);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -