📄 mod_cas.c
字号:
// 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_rerror(APLOG_MARK, APLOG_ERR, r, "mod_cas: certificate error: " "bad cert or verisignserverca.pem is not available.\n"); 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_rerror(APLOG_MARK, APLOG_ERR, r, "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); ap_table_set(r->headers_out, "Location", temp); return HTTP_MOVED_TEMPORARILY; }}/* * Compares the authenticated user (from CAS) with the list of users (and, * in the future, hopefully groups using some sensible mechanism) authorized * by "Require" directives. * * Operates only if the AuthType is CAS, returning DECLINED otherwise on * the chance that another module will validate the request if we can't. * On success, we return OK, which solves the "authorization" question * for the entire request -- on OK, other modules should not be called. * If the AuthType is CAS, though, and the user isn't authorized as far * as we're concerned, we're going to take the initiative and return * HTTP_FORBIDDEN directly (rather than just DECLINED; that is, we're * not going to give any other module a chance to authorize anyone if the * AuthType is CAS). We return HTTP_FORBIDDEN rather than * HTTP_UNAUTHORIZED because we don't want the browser to think it's * just temporarily failed and conduct a Basic Authentication dialog with * the user. */static int authorize(request_rec *r){ const array_header *requirements_array; const char *auth_type = ap_auth_type(r); const char *authenticated_user = r->connection->user; const char *req_line, *req_word; require_line *requirements; int i, method; /* Someone else has to pick up the torch if "CAS" isn't our AuthType. */ if (auth_type && strcmp(auth_type, "CAS")) return DECLINED; /* Some design considerations here were informed by mod_auth_kerb */ requirements_array = ap_requires(r); if (!requirements_array) return OK; // no restriction requirements = (require_line *) requirements_array->elts; method = r->method_number; for (i = 0; i < requirements_array->nelts; i++) { // if our method isn't covered by the requirements line, ignore it if (!(requirements[i].method_mask & (1 << method))) { continue; } req_line = requirements[i].requirement; // full line req_word = ap_getword(r->pool, &req_line, ' '); // individual word /* * If the requirements simply call for a "valid user," we can * simply return success, because if we're called, it means that * CAS authentication performed in an earlier callback DIDN'T * redirect back to CAS, so we have a valid user already, either * by virtue of a ticket or of having stored authenticated status * earlier. */ if (!strcmp(req_word, "valid-user")) return OK; else if (!strcmp(req_word, "user")) { // the rest of the words are individual user names, so see // if our authenticated user matches while (*req_line) { req_word = ap_getword_conf(r->pool, &req_line); if (!strcmp(req_word, authenticated_user)) return OK; } } else if (!strcmp(req_word, "group")) { // the rest of the words are individual group names while (*req_line) { req_word = ap_getword_conf(r->pool, &req_line); if (is_user_in_group(authenticated_user, req_word, r->pool)) return OK; } } } // For now, no other sorts of requirements are supported // For the moment, fail cleanly if we get something like // "Require group foo" so that configuration files don't // need to be changed; the relevant line can just be ignored, // and we failed "closed." return HTTP_FORBIDDEN;}/* Utility function definitions *//* * Fills buf with the 'service' portion of the URL requested. This will * be the entire URL minus any trailing '&ticket=', if present. * The URL is determined here, heuristically, from the request record. * If buf isn't large enough to fit the entire URL, NULL is returned. */static char *get_service(request_rec *r, char *buf, int buflen) { int l; char portstr[6]; char *path = r->uri, *p; mod_cas_conf *c = (mod_cas_conf *) ap_get_module_config( r->server->module_config, &cas_module); // /* // * For the server name, use the "Host" header if it exists; otherwise, // * consult the 'server' record. Note that our 'server' string includes // * the server's name and an optional ':port'. // */ // const char *server = ap_table_get(r->headers_in, "Host"); // char *query = r->args; // char *scheme = "http://"; // if (!server) // server = r->server->server_hostname; // if (!query) // query = ""; /* * Avoid using the "Host" header, for it's under the control of * the client and may be forged unless Apache's administrator * goes out of its way to prevent this. */ const char *server = r->server->server_hostname; char *query = r->args; char *scheme = "http://"; if (!query) query = ""; if (!(c->CASLocalCacheInsecure)) scheme = "https://"; LOG("server = '"); LOG(server); LOG("'\n"); LOG("query = '"); LOG(query); LOG("'\n"); LOG("scheme = '"); LOG(scheme); LOG("'\n"); /* convert the port number to a string in case we need it */ snprintf(portstr, 6*sizeof(char), "%d", r->server->port); LOG("portstr: "); LOG(portstr); LOG("\n"); if ((l = strlen(scheme) + strlen(server) + strlen(":") + strlen(portstr) + strlen(path) + strlen(query) + 1) > buflen) return NULL; strcpy(buf, scheme); strcat(buf, server); /* add the port number if it's non-standard */ if(c->CASLocalCacheInsecure && r->server->port != 80 || !c->CASLocalCacheInsecure && r->server->port != 443) { strcat(buf, ":"); strcat(buf, portstr); } strcat(buf, path); if (query && *query != '\0') { strcat(buf, "%3F"); /* "?" */ for (p = query; *p; p++) { if (*p == '=') strcat(buf, "%3D"); else if (*p == '?') strcat(buf, "%3F"); else if (*p == '&') strcat(buf, "%26"); /* rewrite later to be more complete */ else { char t[2]; t[0] = *p; t[1] = '\0'; strcat(buf, t); } } } /* eliminate the ticket */ if ((p = strstr(buf, "%3Fticket%3D"))) *p = '\0'; if ((p = strstr(buf, "%26ticket%3D"))) *p = '\0'; return buf;}/* * Returns the 'ticket' portion of the URL requested. This consists * of everything following '&ticket=' in the logical full URL. If no "ticket" * parameter was specified, we return NULL. */static char *get_ticket(request_rec *r) { char *ticket = NULL; if (r->args && *(r->args) != '\0') { /* if the query starts with "ticket=", then use the whole query */ if (r->args == strstr(r->args, "ticket=")) ticket = r->args + strlen("ticket="); else { /* otherwise, we need an "&" */ ticket = strstr(r->args, "&ticket="); if (ticket) ticket += strlen("&ticket="); } } if (!ticket || *ticket == '\0') return NULL; return ticket;}/* Functions for handling the ticket cache. *//* * Add an entry to the ticket cache. Values are copied from the TicketEntry, * which need not persist after the call terminates. */static void cache_put(TicketEntry te) { int next; char *last_slot = ticket_cache + ticket_cache_size - sizeof(TicketEntry); if (!ticket_cache) return; // alas, nothing to do LOG("in cache_put()\n"); // get an advisory, exclusive lock on the ticket cache file write_lock(ticket_cache_fd); LOG("-> got lock\n"); // the first int of the ticket cache stores the next slot memcpy(&next, ticket_cache, sizeof(int)); LOG("-> first memcpy() done\n"); // wrap around if necessary if (ticket_cache + sizeof(int) + next * sizeof(TicketEntry) > last_slot) next = 0; memcpy(ticket_cache + sizeof(int) + next * sizeof(TicketEntry), &te, sizeof(TicketEntry)); msync(ticket_cache + sizeof(int) + next * sizeof(TicketEntry), sizeof(TicketEntry), MS_SYNC | MS_INVALIDATE); LOG("-> second memcpy() done\n"); // increment next, letting our successor handle wraparound next++; memcpy(ticket_cache, &next, sizeof(int)); msync(ticket_cache, sizeof(int), MS_SYNC | MS_INVALIDATE); // release our lock un_lock(ticket_cache_fd); LOG("-> released lock\n");}/* * Retrieves the NetID for a given ticket, storing it in the buffer * provided. Returns 1 if the ticket was found and valid, 0 otherwise. * The 'insecure' parameter stores our current CASLocalCacheInsecure * status. */static int cache_get(char *ticket, char *buf, int buflen, char insecure) { char *p; char *last_slot = ticket_cache + ticket_cache_size - sizeof(TicketEntry); TicketEntry t; LOG("in cache_get()\n"); // get an advisory, shared file lock on the ticket cache file read_lock(ticket_cache_fd); // the first few bytes of the ticket array form an integer we don't // care about for (p = ticket_cache + sizeof(int); p <= last_slot; p += sizeof(TicketEntry)) { memcpy(&t, p, sizeof(TicketEntry)); if (t.valid && t.secure == !insecure && time(NULL) < t.expiration && !strcmp(t.ticket, ticket)) { if (strlen(t.netid) + 1 > buflen) return 0; strcpy(buf, t.netid); un_lock(ticket_cache_fd); // release the lock return 1; } } un_lock(ticket_cache_fd); // release the lock return 0;}/* * Constructs a random ticket value and stores it in the buffer provided. * The buffer is terminated with '\0'. On unexpected failure, return 0; * otherwise, return 1. */static int random_ticket(char *buf, int buflen, char *egdfile) { int fd; char *pool = "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "abcdefghijklmnopqrstuvwxyz" "01234567890"; LOG("in random_ticket()\n"); if (egdfile == NULL) { /* Get our randomness from /dev/urandom. */ LOG("using /dev/urandom\n"); if ((fd = open("/dev/urandom", O_RDONLY)) < 0) { LOG("ERROR! Couldn't open /dev/urandom. Do not deploy!"); return 0; } else if (read(fd, buf, buflen - 1) < 0) { close(fd); LOG("ERROR! Couldn't open /dev/urandom. Do not deploy!"); return 0; } else {
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -