예제 #1
0
/** 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;
}
예제 #2
0
/** 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;
}