#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 #include #include #include #ifndef LDAP_PORT #define LDAP_PORT 389 #endif /* LDAP_PORT */ #define DEBUG_LDAP 1 module MODULE_VAR_EXPORT info_share_ldap_auth_module; typedef struct _info_share_auth_config_rec { LDAP *ld; char *ldap_server, *base_dn, *uid_attr, *group_attr; char filter[512]; char *user_dn; char *bind_dn, *bind_pass; int ldap_port; int auth_ldapauthoritative; char *dummy; } info_share_ldap_auth_config_rec; static void *create_info_share_ldap_auth_dir_config(pool *p,char *d) { info_share_ldap_auth_config_rec *cr; cr=(info_share_ldap_auth_config_rec *) ap_pcalloc(p,sizeof(info_share_ldap_auth_config_rec)); if (cr == (info_share_ldap_auth_config_rec *) NULL) return (NULL); cr->ld=(LDAP *) NULL; cr->ldap_server=ap_pstrdup(p,"ncdcrx1.uccs.edu"); /* used to be localhost, mm, Aug-20-1999 */ #if 1 cr->base_dn=ap_pstrdup(p,"o=EAS"); #else cr->base_dn=NULL; #endif cr->user_dn=NULL; cr->bind_dn=NULL; cr->bind_pass=NULL; cr->uid_attr=ap_pstrdup(p,"uid"); cr->group_attr=ap_pstrdup(p,"uniqueMember"); cr->ldap_port=LDAP_PORT; cr->auth_ldapauthoritative=0; /* fortress is secure by default */ return (cr); } static const char *set_ldap_server(cmd_parms *cmd,info_share_ldap_auth_config_rec *cr, char *arg) { cr->ldap_server=ap_pstrdup(cmd->pool,arg); return (NULL); } static const char *set_ldap_port(cmd_parms *cmd,info_share_ldap_auth_config_rec *cr, char *arg) { cr->ldap_port=atoi(arg); return (NULL); } static const char *set_base_dn(cmd_parms *cmd,info_share_ldap_auth_config_rec *cr, char *arg) { cr->base_dn=ap_pstrdup(cmd->pool,arg); return (NULL); } static const char *set_group_attr(cmd_parms *cmd,info_share_ldap_auth_config_rec *cr, const char *arg) { cr->group_attr=ap_pstrdup(cmd->pool,arg); return (NULL); } static const char *set_bind_dn(cmd_parms *cmd,info_share_ldap_auth_config_rec *cr, char *arg) { cr->bind_dn=ap_pstrdup(cmd->pool,arg); return (NULL); } static const char *set_bind_pass(cmd_parms *cmd,info_share_ldap_auth_config_rec *cr, char *arg) { cr->bind_pass=ap_pstrdup(cmd->pool,arg); return (NULL); } static const char *set_uid_attr(cmd_parms *cmd,info_share_ldap_auth_config_rec *cr, char *arg) { cr->uid_attr=ap_pstrdup(cmd->pool,arg); return (NULL); } static const char *set_ldapauthoritative(cmd_parms *cmd,info_share_ldap_auth_config_rec *cr,char *arg) { char *a=ap_pstrdup(cmd->pool,arg); if (a != NULL) { if ((strcmp(a,"no") == 0) || (strcmp(a,"off") == 0)) { #ifdef DEBUG_LDAP ap_log_error(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,NULL, "[mod_auth_ldap.c] - AuthLDAPAuthoritative is off"); #endif /* DEBUG_LDAP */ cr->auth_ldapauthoritative=0; } else { if ((strcmp(a,"yes") == 0) || (strcmp(a,"on") == 0)) { #ifdef DEBUG_LDAP ap_log_error(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,NULL, "[mod_auth_ldap.c] - AuthLDAPAuthoritative is on"); #endif /* DEBUG_LDAP */ cr->auth_ldapauthoritative=1; #ifdef DEBUG_LDAP } else { ap_log_error(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,NULL, "[mod_auth_ldap.c] - AuthLDAPAuthoritative has unexpected argument"); #endif /* DEBUG_LDAP */ } } #ifdef DEBUG_LDAP } else { ap_log_error(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,NULL, "[mod_auth_ldap.c] - AuthLDAPAuthoritative is missing its argument"); #endif /* DEBUG_LDAP */ } return (NULL); } char * mystrstr(string, substring) register char *string; /* String to search. */ char *substring; /* Substring to try to find in string. */ { register char *a, *b; b = substring; if (*b == 0) { return string; } for ( ; *string != 0; string += 1) { if (*string != *b) { continue; } a = string; while (1) { if (*b == 0) { return string; } if (*a++ != *b++) { break; } } b = substring; } return (char *) 0; } char *findMatch(const char *string, const char *startsubstring, const char *endsubstring) { char *startptr; char *endptr; startptr = (char *) 0; endptr = (char *) 0; startptr = mystrstr(string, startsubstring); if (startptr == (char *) 0) return (char *) 0; endptr = mystrstr(startptr, endsubstring); if (endptr == (char *) 0) return (char *) 0; return ( (char *)strndup(startptr, endptr-startptr)); } static const command_rec info_share_ldap_auth_cmds[]= { {"LDAP_Server",set_ldap_server,NULL,OR_AUTHCFG,TAKE1,"LDAP server"}, {"LDAP_Port", set_ldap_port, NULL,OR_AUTHCFG,TAKE1,"LDAP port"}, {"Base_DN", set_base_dn, NULL,OR_AUTHCFG,TAKE1,"Base DN"}, {"UID_Attr", set_uid_attr, NULL,OR_AUTHCFG,TAKE1,"uid"}, {"Group_Attr", set_group_attr, NULL,OR_AUTHCFG,TAKE1, "Group attribute"}, {"Bind_DN", set_bind_dn, NULL,OR_AUTHCFG,TAKE1,"Bind DN"}, {"Bind_Pass", set_bind_pass, NULL,OR_AUTHCFG,TAKE1,"Bind Password"}, {"AuthLDAPAuthoritative",set_ldapauthoritative,NULL,OR_AUTHCFG,TAKE1, "Set to no to allow control to be passed along to lower modules if the uid is not known to this module"}, {NULL} }; /* ** ldapFindUserDN() ** return the DN of the user ** ** Parameters: ** LDAP *ld valid handle to LDAP ** char *base_dn LDAP base Distinguised Name ** char *uid_attrib LDAP uid attribute, e.g. "uid" ** char *userid the userid to check ** request_rect *r needed for writing log in Win2K ** char *bind_dn Bind_DN, can be NULL ** char *bind_pass Bind_Pass, can be NULL ** ** Return Values: ** pointer to DN of the userid if found, NULL otherwise ** ** Limitations and Comments: ** ld must be valid. ** ** any LDAP sdk should work. returns pointer to a malloc'd space, the ** caller is responsible to free it. ** ** Development History: ** who when why ** ma_muquit@fccc.edu Nov-29-1998 first cut for my own web server ** mhttpd ** muquit@muquit.com Mar-15-2001 bind with bind and pass if provided. */ static char *ldapFindUserDN(LDAP *ld,char *base_dn,char *uid_attrib, char *userid,request_rec *r,char *bind_dn,char *bind_pass) { int count = 0; int bound=0, rc=(-1); LDAPMessage *result, *entry; char *dn, szfilter[256]; result=(LDAPMessage *) NULL; entry=(LDAPMessage *) NULL; dn=(char *) NULL; /* prepare filter with UidAttr. */ ap_snprintf(szfilter,sizeof(szfilter)-1,"(%s=%s)",uid_attrib,userid); #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - filter: %s",szfilter); #endif /* DEBUG_LDAP */ /* ** if the bind DN and bind password is specified, try to bind with ** them first. */ if (bind_dn != NULL && bind_pass != NULL) { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - trying to bind with bind DN \"%s\" and password (not shown)",bind_dn); #endif /* DEBUG_LDAP */ if (ldap_simple_bind_s(ld,bind_dn,bind_pass) == LDAP_SUCCESS) { bound=1; #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - Bound successfully with DN \"%s\" and password (not shown)",bind_dn); #endif /* DEBUG_LDAP */ } else { ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - Warning: bind with DN \"%s\" and password (not shown) did not succeed",bind_dn); } } else { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - Bind_DN/Bind_Pass is not provided"); #endif /* DEBUG_LDAP */ } /* Bind anonymously now only if not bound yet. */ if (bound == 0) { if (ldap_simple_bind_s(ld,base_dn,NULL) != LDAP_SUCCESS) { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - ldap_simple_bind_s() failed"); #endif /* DEBUG_LDAP */ return ((char *) NULL); } } /* search to find the dn for the user */ rc=ldap_search_s(ld, base_dn, LDAP_SCOPE_SUBTREE, szfilter, NULL, 0, &result); if (rc != LDAP_SUCCESS) { ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - ldap_search_s() failed"); /* note: ldap_err2string() returns pointer to a static space */ ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - Error: %s",ldap_err2string(rc)); return (NULL); } /* found something, better be a single match */ if ((count = ldap_count_entries(ld,result)) != 1) { if (count > 0) { ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - multiple matches found for: %s",userid); } else { if (bound == 0) { ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - do we have anonymous access? no user found for: %s",userid); } else { ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - no user found for: %s",userid); } } if (result != (LDAPMessage *) NULL) ldap_msgfree(result); return (NULL); } if ((entry=ldap_first_entry(ld,result)) == NULL) { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - %s=%s Unknown in LDAP server",uid_attrib,userid); #endif /* DEBUG_LDAP */ if (result != (LDAPMessage *) NULL) ldap_msgfree(result); return (NULL); } if ((dn=ldap_get_dn(ld,entry)) == NULL) { ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - could not obtain DN from LDAP for user %s",userid); return (NULL); } #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - in ldapFindUserDN() user DN: %s",dn); #endif /* DEBUG_LDAP */ return (dn); /* caller must free it */ } /* ** remove all whitespace (leading or trailing) from a string. ** borrowed from public domain c snippets library. ** muquit, sep-07-1998 */ static char *rmallws(char *str) { char *obuf, *nbuf; if (str) { for (obuf=str, nbuf=str; *obuf; ++obuf) { if (!isspace(*obuf)) *nbuf++ = *obuf; } *nbuf='\0'; } return (str); } /* for my use only - muquit@muquit.com */ #ifdef MY_DEBUG static void dumpResult(LDAP *ld,LDAPMessage *res,request_rec *r) { LDAPMessage *ent; BerElement *ber; int i; char *attr, **vals; ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "XXXXXXXX"); for (ent=ldap_first_entry(ld,res); ent != NULL; ent=ldap_next_entry(ld,ent)) { for (attr=ldap_first_attribute(ld,ent,&ber); attr != NULL; attr=ldap_next_attribute(ld,ent,ber)) { if (strcasecmp(attr,"jpegphoto") != 0) { if ((vals=ldap_get_values(ld,ent,attr)) != NULL) { for (i=0; vals[i] != NULL; i++) { ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "dump: %s=%s",attr,vals[i]); } if (vals) ldap_value_free(vals); } } } } } #endif /* MY_DEBUG */ /* ** validate the user id via LDAP ** Returns: ** DECLINED if ldap server is not defined in config file ** AUTH_REQUIRED for any other kind of errors ** OK if authorized */ static int info_share_ldap_authenticate_user(request_rec *r, info_share_ldap_auth_config_rec *cr, char *user) { int rc, res; char *dn, szfilter[256], sztdn[256]; /* if no user is specified, keep asking */ if (user == NULL || *user == '\0') { return(-1); } #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - LDAP server=%s,Port=%d",cr->ldap_server,cr->ldap_port); #endif /* DEBUG_LDAP */ /* mm, aug-20-199 */ if (cr->ldap_server == NULL) return (-1); if (cr->ld != NULL) { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - closing connection to LDAP server: %s", cr->ldap_server); #endif /* DEBUG_LDAP */ ldap_unbind_s(cr->ld); } #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "mod_auth_ldap.c] - opening connection to LDAP server: %s at port: %d",cr->ldap_server,cr->ldap_port); #endif /* DEBUG_LDAP */ /* connect to LDAP server */ cr->ld=ldap_open(cr->ldap_server,cr->ldap_port); if (cr->ld == (LDAP *) NULL) { ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - Could not connect to LDAP server %s at port %d",cr->ldap_server,cr->ldap_port); ap_note_basic_auth_failure(r); return (-1); } /* now get the User DN */ dn=ldapFindUserDN(cr->ld,cr->base_dn,cr->uid_attr,user,r, cr->bind_dn,cr->bind_pass); if (dn == (char *) NULL) { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - ldapFindUserDN() didn't return any DN for user \"%s\" with attr \"%s\"",user,cr->uid_attr); #endif /* DEBUG_LDAP */ } /* store user DN, we'll use it later if needed*/ cr->user_dn=ap_pstrdup(r->pool,dn); #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - User DN: %s",dn); #endif /* DEBUG_LDAP */ /* free things we won't need */ if (dn != (char *) NULL) { /* * if free is called, it dies in Windows 2000 if compiled with * iPlanet C SDK 5.08. ldap_memfree() is suggested use with most of * the SDKs anyway. * - muquit@muquit.com Sep-24-2002 */ /* (void) free(dn);*/ ldap_memfree(dn); dn=NULL; } return (1); } static int info_share_ldap_authenticate_basic_user(request_rec *r, info_share_ldap_auth_config_rec *cr) { char *user; user=strdup(findMatch(cr->dummy, "CN", "/") + 3); #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - LDAP server=%s,Port=%d",cr->ldap_server,cr->ldap_port); #endif /* DEBUG_LDAP */ #if 1 if (info_share_ldap_authenticate_user(r, cr, user) < 0 ) { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - Invalid Login or Password for:%s",user); #endif /* DEBUG_LDAP */ } else { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_auth_ldap.c] - valid Login or Password for:%s",user); #endif /* DEBUG_LDAP */ } #endif free (user); return OK; } #if 0 static int info_share_ldap_authenticate_basic_user(request_rec *r) { return OK; } #endif /* called to check to see if resource being requested requires authorization. */ static int info_share_ldap_check_user_access(request_rec *r) { const array_header *reqs_arr; require_line *reqs; register int x; int rc, m; const char *t, *w; info_share_ldap_auth_config_rec *cr; conn_rec *c; char *attr, *user=r->connection->user; cr=(info_share_ldap_auth_config_rec *) NULL; reqs_arr=(array_header *)NULL; reqs=(require_line *) NULL; cr=(info_share_ldap_auth_config_rec *) ap_get_module_config(r->per_dir_config,&info_share_ldap_auth_module); reqs_arr=ap_requires(r); reqs=reqs_arr ? (require_line *) reqs_arr->elts : NULL; m=r->method_number; c=r->connection; /* check if the request is non-SSL Decline request if it is not SSL. otherwise, forbid access. */ if (!ap_ctx_get(r->connection->client->ctx, "ssl")) { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_info_share_auth_ldap.c] - not an SSL connection"); #endif /* DEBUG_LDAP */ return HTTP_FORBIDDEN; } SSL *ssl = (SSL *) ap_ctx_get(r->connection->client->ctx, "ssl"); SSL_SESSION *session = (SSL_SESSION *) SSL_get_session(ssl); if (session) { X509 *peer = session->peer; if (!peer) { #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_info_share_auth_ldap.c] - peer certificate not found"); #endif /* DEBUG_LDAP */ return HTTP_FORBIDDEN; } { char buf[1024]; X509_NAME_oneline( X509_get_subject_name(peer), buf, sizeof buf); #ifdef DEBUG_LDAP ap_log_rerror(APLOG_MARK,APLOG_NOERRNO | APLOG_ERR,r, "[mod_info_share_auth_ldap.c] - subject \"%s\" ",buf); #endif /* DEBUG_LDAP */ cr->dummy=ap_pstrdup(r->pool, buf); } } info_share_ldap_authenticate_basic_user(r, cr); return OK; } #if 0 /*--------------------------------------------------------------------------*/ /* Finally, the list of callback routines and data structures that */ /* provide the hooks into our module from the other parts of the server. */ /*--------------------------------------------------------------------------*/ module ldap_auth_module = { STANDARD_MODULE_STUFF, NULL, /* module initializer */ create_info_share_ldap_auth_dir_config, /* per-directory config creator */ NULL, /* dir config merger */ NULL, /* server config creator */ NULL, /* server config merger */ info_share_ldap_auth_cmds, /* command table */ NULL, /* [7] list of handlers */ NULL, /* [2] filename-to-URI translation */ NULL, /* [5] check/validate user_id */ NULL, /* [6] check auth */ info_share_ldap_check_user_access, /* [4] check access by host address */ NULL, /* [7] MIME type checker/setter */ NULL, /* [8] fixups */ NULL, /* [10] logger */ NULL, /* [3] header parser */ NULL, /* process initializer */ NULL, /* process exit/cleanup */ NULL /* [1] post read_request handling */ }; info_share_ldap_authenticate_basic_user, /* [6] check auth */ #else /*--------------------------------------------------------------------------*/ /* Finally, the list of callback routines and data structures that */ /* provide the hooks into our module from the other parts of the server. */ /*--------------------------------------------------------------------------*/ module info_share_ldap_auth_module = { STANDARD_MODULE_STUFF, NULL, /* module initializer */ create_info_share_ldap_auth_dir_config, /* per-directory config creator */ NULL, /* dir config merger */ NULL, /* server config creator */ NULL, /* server config merger */ info_share_ldap_auth_cmds, /* command table */ NULL, /* [7] list of handlers */ NULL, /* [2] filename-to-URI translation */ NULL, /* [4] check access by host address */ NULL, /* [5] check/validate user_id */ info_share_ldap_check_user_access, /* [6] check auth */ NULL, /* [7] MIME type checker/setter */ NULL, /* [8] fixups */ NULL, /* [10] logger */ NULL, /* [3] header parser */ NULL, /* process initializer */ NULL, /* process exit/cleanup */ NULL /* [1] post read_request handling */ }; #endif