/** Returns status of ticket by filling 'buf' with a NetID if the ticket * is valid and buf is large enough and returning 1. If not, 0 is * returned. */ int cas_validate( char *ticket, char *service, char *outbuf, int outbuflen, char *proxies[]) { int s = 0, err, b, ret, total; struct sockaddr_in sa; struct hostent h, *hp2; SSL_CTX *ctx = NULL; SSL *ssl = NULL; X509 *s_cert = NULL; char buf[4096]; SSL_METHOD *method = NULL; char *full_request, *str, *tmp; char netid[14]; char parsebuf[128]; int i; SSLeay_add_ssl_algorithms(); method = SSLv23_client_method(); SSL_load_error_strings(); ctx = SSL_CTX_new(method); if (!ctx) END(CAS_SSL_ERROR_CTX); if ((s = socket(AF_INET, SOCK_STREAM, 0)) == -1) END(CAS_ERROR_CONN); memset(&sa, 0, sizeof(sa)); sa.sin_family = AF_INET; hp2 = gethostbyname(CAS_HOST); memcpy(&h, hp2, sizeof(h)); // gethostbyname_r(CAS_HOST, &h, buf, sizeof(buf), &hp2, &b); memcpy(&(sa.sin_addr.s_addr), h.h_addr_list[0], sizeof(long)); sa.sin_port = htons(CAS_PORT); if (connect(s, (struct sockaddr*) &sa, sizeof(sa)) == -1) END(CAS_ERROR_CONN); if (!(ssl = SSL_new(ctx))) END(CAS_SSL_ERROR_CTX); if (!SSL_set_fd(ssl, s)) END(CAS_SSL_ERROR_CTX); if (! (err = SSL_connect(ssl))) END(CAS_SSL_ERROR_CONN); if (!(s_cert = SSL_get_peer_certificate(ssl))) END(CAS_SSL_ERROR_CERT); if (!valid_cert(s_cert, CAS_HOST)) END(CAS_SSL_ERROR_CERT); X509_free(s_cert); full_request = malloc(strlen(CAS_METHOD) + strlen(" ") + strlen(CAS_VALIDATE) + strlen("?ticket=") + strlen(ticket) + + strlen("&service=") + strlen(service) + strlen(" ") + strlen(CAS_PROT) + strlen("\n\n") + 1); sprintf(full_request, "%s %s?ticket=%s&service=%s %s\n\n", CAS_METHOD, CAS_VALIDATE, ticket, service, CAS_PROT); if (!SSL_write(ssl, full_request, strlen(full_request))) END(CAS_SSL_ERROR_HTTPS); total = 0; do { b = SSL_read(ssl, buf + total, (sizeof(buf) - 1) - total); total += b; } while (b > 0); buf[total] = '\0'; if (b != 0 || total >= sizeof(buf) - 1) END(CAS_SSL_ERROR_HTTPS); // unexpected read error or response too large str = (char *)strstr(buf, "\r\n\r\n"); // find the end of the header if (!str) END(CAS_SSL_ERROR_HTTPS); // no header /* * 'str' now points to the beginning of the body, which should be an * XML document */ // make sure that the authentication succeeded if (!element_body( str, "cas:authenticationSuccess", 1, parsebuf, sizeof(parsebuf))) { LOG("authentication failure\n"); LOG(str); LOG("\n"); END(CAS_AUTHENTICATION_FAILURE); } // retrieve the NetID if (!element_body(str, "cas:user", 1, netid, sizeof(netid))) { LOG("unable to determine username\n"); END(CAS_PROTOCOL_FAILURE); } // check the first proxy (if present) if (element_body(str, "cas:proxies", 1, parsebuf, sizeof(parsebuf))) if (element_body(str, "cas:proxy", 1, parsebuf, sizeof(parsebuf))) if (!arrayContains(proxies, parsebuf)) { LOG("bad proxy: "); LOG(parsebuf); LOG("\n"); END(CAS_BAD_PROXY); } /* * without enough space, fail entirely, since a partial NetID could * be dangerous */ if (outbuflen < strlen(netid) + 1) { LOG("output buffer too short\n"); END(CAS_PROTOCOL_FAILURE); } strcpy(outbuf, netid); SUCCEED; /* cleanup and return */ end: if (ssl) SSL_shutdown(ssl); if (s > 0) close(s); if (ssl) SSL_free(ssl); if (ctx) SSL_CTX_free(ctx); return ret; }
/** Returns status of ticket by filling 'buf' with a NetID if the ticket * is valid and buf is large enough and returning 1. If not, 0 is * returned. */ int cas_validate( char *ticket, char *service, char *outbuf, int outbuflen, pam_cas_config_t *config) { int b, ret, total; SSL_CTX *ctx = NULL; BIO * bio = NULL; SSL *ssl = NULL; char buf[4096]; char *full_request = NULL, *str; char netid[CAS_LEN_NETID]; char parsebuf[128]; debug = config->debug; if (config->ssl) { DEBUG_LOG("We use SSL as configured\n", ""); /* Set up the SSL library */ ERR_load_BIO_strings(); SSL_load_error_strings(); OpenSSL_add_all_algorithms(); #if defined(OpenSSL_add_ssl_algorithms) OpenSSL_add_ssl_algorithms(); #endif /* Set up the SSL context */ ctx = SSL_CTX_new(SSLv23_client_method()); if ( ! ctx ) { DEBUG_LOG("Cannot create SSL context", ""); END(CAS_SSL_ERROR_INIT); } /* Load the trust store */ if(! SSL_CTX_load_verify_locations(ctx, config->trusted_ca, NULL)) { DEBUG_LOG("Error loading certificate store : %s\n", ERR_reason_error_string(ERR_get_error())); END(CAS_SSL_ERROR_CERT_LOAD); } /* Setup the connection */ bio = BIO_new_ssl_connect(ctx); /* Set the SSL_MODE_AUTO_RETRY flag : if the server suddenly wants a new handshake, OpenSSL handles it in the background */ BIO_get_ssl(bio, & ssl); SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY); /* Create and setup the connection */ DEBUG_LOG("We connect to host %s\n", config->host); BIO_set_conn_hostname(bio, config->host); BIO_set_conn_port(bio, config->port); if(BIO_do_connect(bio) <= 0) { DEBUG_LOG("Error attempting to connect : %s\n", ERR_reason_error_string(ERR_get_error())); END(CAS_SSL_ERROR_CONN); } /* Check the certificate */ if (SSL_get_verify_result(ssl) != X509_V_OK) { DEBUG_LOG("Certificate verification error: %ld\n", SSL_get_verify_result(ssl)); END(CAS_SSL_ERROR_CERT_VALID); } } else /* no ssl */ { bio = BIO_new_connect(config->host); BIO_set_conn_port(bio, config->port); if(BIO_do_connect(bio) <= 0) { DEBUG_LOG("Error attempting to connect : %s\n", config->host); END(CAS_ERROR_CONN); } } /* build request */ full_request = malloc(strlen(CAS_METHOD) + strlen(" ") + strlen(config->uriValidate) + strlen("?ticket=") + strlen(ticket) + + strlen("&service=") + strlen(service) + strlen(" ") + strlen(GENERIC_HEADERS) + strlen ("\r\n") #ifdef HEADER_HOST_NAME + strlen(HEADER_HOST_NAME) + strlen (": ") + strlen (config->host) #endif + strlen("\r\n\r\n") + 1); if (full_request == NULL) { DEBUG_LOG("Error memory allocation%s\n", ""); END(CAS_ERROR_MEMORY_ALLOC); } #ifdef HEADER_HOST_NAME sprintf(full_request, "%s %s?ticket=%s&service=%s %s\r\n%s: %s\r\n\r\n", CAS_METHOD, config->uriValidate, ticket, service, GENERIC_HEADERS, HEADER_HOST_NAME,config->host); #else sprintf(full_request, "%s %s?ticket=%s&service=%s %s\r\n\r\n", CAS_METHOD, config->uriValidate, ticket, service, GENERIC_HEADERS); #endif /* send request */ DEBUG_LOG("---- request :\n%s\n", full_request); if (BIO_write(bio, full_request, strlen(full_request)) != strlen(full_request)) { DEBUG_LOG("Unable to correctly send request to %s\n", config->host); END(CAS_ERROR_HTTP); } /* Read the response */ total = 0; b = 0; do { b = BIO_read(bio, buf + total, (sizeof(buf) - 1) - total); total += b; } while (b > 0); buf[total] = '\0'; if (b != 0 || total >= sizeof(buf) - 1) { DEBUG_LOG("Unexpected read error or response too large from %s\n", config->host); DEBUG_LOG("b = %d\n", b); DEBUG_LOG("total = %d\n", total); DEBUG_LOG("buf = %s\n", buf); END(CAS_ERROR_HTTP); // unexpected read error or response too large } DEBUG_LOG("---- response :\n%s\n", buf); str = (char *)strstr(buf, "\r\n\r\n"); // find the end of the header if (!str) { DEBUG_LOG("no header in response%s\n", ""); END(CAS_ERROR_HTTP); // no header } /* * 'str' now points to the beginning of the body, which should be an * XML document */ // make sure that the authentication succeeded if (!element_body( str, "cas:authenticationSuccess", 1, parsebuf, sizeof(parsebuf))) { END(CAS_BAD_TICKET); } // retrieve the NetID if (!element_body(str, "cas:user", 1, netid, sizeof(netid))) { DEBUG_LOG("unable to determine username%s\n", ""); END(CAS_PROTOCOL_FAILURE); } // check the first proxy (if present) if ((config->proxies) && (config->proxies[0])) if (element_body(str, "cas:proxies", 1, parsebuf, sizeof(parsebuf))) if (element_body(str, "cas:proxy", 1, parsebuf, sizeof(parsebuf))) if (!arrayContains(config->proxies, parsebuf)) { DEBUG_LOG("bad proxy: %s\n", parsebuf); END(CAS_BAD_PROXY); } /* * without enough space, fail entirely, since a partial NetID could * be dangerous */ if (outbuflen < strlen(netid) + 1) { syslog(LOG_ERR, "output buffer too short"); DEBUG_LOG("output buffer too short%s\n", ""); END(CAS_PROTOCOL_FAILURE); } strcpy(outbuf, netid); SUCCEED; /* cleanup and return */ end: if (ctx) SSL_CTX_free(ctx); if (bio) BIO_free_all(bio); if (full_request) free(full_request); return ret; }