Allowing Clarify ClearExpress to authenticate against database
This solution is specific for Netscape/iPlanet users, but i'm sure the approach could be used by other web servers.
Basically what we did was use the Netscape API's (NSAPI) and a custom version of clearexp_cgi to put in our own user database to validate against. In this case, to make it real-time, we actually queried table_web_user. The baseline Clarify approach is to use htpasswd, which can't be updated in real-time with the Clarify tools.
i started with old version of clearexp_cgi which i somehow had source to. However if you don't have source, it's easy enough to replace. i even had a customer once who rewrote it as a servlet. It's basic function is to read connection info from the config file (*.cfg) and open up a socket connection to the machine specified. It then wraps up the user request and the cgi env into a stringbuffer and pass it to the ClearExpress app server and awaits a response. So having said that, i took the clearexp_cgi and extended it with the NSAPI's to set the security tokens it needs to set REMOTE_USER. The authentication piece is performed by a hard-coded query to a specific Clarify web page that queries the web_user table for the user's username/password combination.
Now i'm not going to publish any of the clearexp_cgi code, but here's a code chunk that was inserted to support the NSAPI and call to the Clarify app server:
/* Netscape libraries */
#include "nsapi.h"
#include "frame/log.h"
/* build request */
msg_str[0] = '\0';
strcat(msg_str, "clearexp_cgi;");
strcat(msg_str, "SERVER_NAME=localhost");
strcat(msg_str, "&SERVER_PORT=80");
strcat(msg_str, "&REMOTE_ADDR=127.0.0.1");
strcat(msg_str, "&SERVER_PROTOCOL=HTTP/1.0");
strcat(msg_str, "&REQUEST_METHOD=GET");
strcat(msg_str, "&QUERY_STRING=login_name%3D");
strcat(msg_str, user);
strcat(msg_str, "&REQUEST_URI=/clearexp_cgi/webuser.htm?login_name%3D");
strcat(msg_str, user);
strcat(msg_str, "&SCRIPT_NAME=/clearexp_cgi");
strcat(msg_str, "&PATH_INFO=/webuser.htm");
/* get password back and compare to entered value */
fprintf(fp, "crypt: %s\n", crypt(pw, db_pw));
/* compare pw from web server with pw from clarify */
if (strcmp(db_pw, "NOT_FOUND") == 0) {
fprintf(fp, "login not found\n");
fclose(fp);
return 2;
} else if (strcmp(crypt(pw, db_pw), db_pw) == 0) {
/* passwords match - encrypted */
fprintf(fp, "password match - encrypted\n");
fclose(fp);
return 1;
} else if (strcmp(pw, db_pw) == 0) {
/* passwords match - not encrypted */
fprintf(fp, "password match - not encrypted\n");
fclose(fp);
return 1;
} else {
/* passwords do not match */
fprintf(fp, "NO password match\n");
fclose(fp);
return 0;
}
/* this is the Netscape SAF (Server Application Function) that
* is called by the web server. it will validate a username/password
* against the clarify database.
*/
NSAPI_PUBLIC int clfy_auth(pblock *param, Session *sn, Request *rq)
{
int len;
char hostname[1024];
unsigned short port;
char name[36];
char value[260];
int count;
int result = 0;
int length;
char *buf;
FILE *cfg_fp;
char *http_user_agent;
/* grab user information passed in from web server */
char *user = pblock_findval("user", param);
char *pw = pblock_findval("pw", param);
char *cfg_fn = pblock_findval("userdb", param);
/* Look for the HTTP_USER_AGENT environment variable
- not used
*/
http_user_agent = pblock_findval("user-agent", rq->headers);
/* read the configuration file */
if ((cfg_fp = fopen(cfg_fn, "r")) == NULL) {
web_proxy_bail(0, "cannot read configuration file");
}
count = fscanf(cfg_fp, "%[^:]:%hu", hostname, &port);
if (count != 2) {
(void)fclose(cfg_fp);
web_proxy_bail(0, "configuration file must contain hostname:port");
}
/* execute the request */
result = web_proxy_task(hostname, port, user, pw);
if (result == 0) {
log_error(LOG_SECURITY, "clfy-auth", sn, rq,
"user %s entered wrong password", user);
/* This will cause the enforcement function to ask user again */
return REQ_NOACTION;
} else if (result == 1) {
/* If we return REQ_PROCEED, the username will be accepted */
return REQ_PROCEED;
} else if (result == 2){
/* No match, have it ask them again */
log_error(LOG_SECURITY, "clfy-auth", sn, rq,
"unknown user %s", user);
return REQ_NOACTION;
} else {
/* Returning generic HTML error page */
/* Get rid of internal content type - usually the
SAF's magnus-internal content type. */
param_free(pblock_remove("content-type", rq->srvhdrs));
/* Set content type to html. */
pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
/* Set status to OK. */
protocol_status(sn, rq, PROTOCOL_OK, NULL);
/* Send the headers and get ready for sending the page. */
protocol_start_response(sn, rq);
/* MALLOC a rough size for buffer. */
buf = (char *) MALLOC(100 * sizeof(char));
/* Put all the variables and the rest of the HTML page
information into one buffer. */
length = util_sprintf(buf,
"<HTML><HEAD>"
"<TITLE>Authorization Proxy Error</TITLE>"
"</HEAD>\n"
"<BODY><H1>"
"Internal Error</H1>\n"
"Error code: %d\n"
"</BODY></HTML>\n", result-10);
/* Send the information to the client. Also check
for any error while sending the page to the client.
If an error occurred, exit. */
if(net_write(sn->csd, buf, length) == IO_ERROR)
{
return REQ_EXIT;
}
/* Return success to the server. */
/*
return REQ_ABORTED;
return REQ_NOACTION;
*/
return REQ_PROCEED;
}
}
ceDbAuth.zip
Posted at 12:00AM Mar 12, 2007 by Robert Eister in Software | Comments[2]
Thanks,
Bob
Posted by Bob Leach on July 17, 2008 at 11:47 AM PDT #
Just recently we ported this to Apache 2 using mod_auth_external. We did a preliminary port to Apache 2.2 also using mod_auth_external, but we didn't take that to production.
Posted by Robert Eister on July 17, 2008 at 08:12 PM PDT #