diff --git a/Makefile b/Makefile index 28cdaa2..18a6f53 100755 --- a/Makefile +++ b/Makefile @@ -15,6 +15,7 @@ APACHECTL=apachectl #DEFS=-Dmy_define=my_value #INCLUDES=-Imy/include/dir #LIBS=-Lmy/lib/dir -lmylib +LIBS=absec_pam # the default target all: local-shared-build diff --git a/absec_authen.h b/absec_authen.h new file mode 100644 index 0000000..4143db2 --- /dev/null +++ b/absec_authen.h @@ -0,0 +1,20 @@ +//////// +// +// header file for authentification +// used either for absec_etc or absec_pam +// +// author: Jean-Luc Cyr +// date: 2018-10 +// +// usage: include this file as header definition +// then link the program with either -labsec_etc or -labsec_pam +// +#define PAM_OK 0 +#define PAM_ERROR_START -1 +#define PAM_ERROR_INVALID_CRED -2 +#define PAM_ERROR_STOP -3 + +#define HTTP_UNAUTHORIZED 401 +#define OK 200 + +int check_user(char* user, char* pass); diff --git a/absec_etc.c b/absec_etc.c new file mode 100644 index 0000000..2b9d9ed --- /dev/null +++ b/absec_etc.c @@ -0,0 +1,114 @@ +///// +// +// File : absec_etc.c +// Author : Jean-Luc Cyr +// Date : 2018-10 +// +// Description: Using /etc/passwd, /etc/shadow, /etc/group as authentification method +// +// Note : doc for using APR in code : https://people.apache.org/~rooneg/talks/portable-c-with-apr/apr.html +// + +#include "absec_auth.h" + +//#include "httpd.h" +//#include "http_config.h" +//#include "http_core.h" +//#include "http_protocol.h" +//#include "ap_config.h" +#include "apr_base64.h" +#include "apr_strings.h" +#include "apr_portable.h" +#include "apr_user.h" + +#include +#include +#include +#include +#include +#include "apr_want.h" + +#include +#include + +//////////////////////////////////////////////////////////////// +// Unix file based auth +int check_user(char* user, char* pass) { +//////// +// Get UID, GIDs for the user +/* Working example, but just UID not PW */ + apr_status_t ret; + apr_uid_t i; + apr_gid_t g; + apr_pool_t *pool; + + apr_initialize(); + apr_pool_create(&pool, NULL); + ret = apr_uid_get( &i, &g, user, pool ); + printf( "Result2: G:%d, I:%d \n
", g,i); + +//////// +/* Retrieve PW from /etc/passwd */ +/* Should include */ + struct passwd *pw; + if((pw = getpwnam(user)) == NULL) + { + printf( "NULL \n
"); + return HTTP_UNAUTHORIZED; + } + else + { + printf( "Unix PW : %s \n
", pw->pw_passwd); + } + +//////// +/* Retrieve PW from /etc/shadow */ +/* Should include */ + struct spwd *spw; + errno = 0; + if((spw = getspnam(user)) == NULL) + { + printf("NULL %d\n
", errno); + return HTTP_UNAUTHORIZED; + } + else + { + printf( "Shadow PW : %s \n
", spw->sp_pwdp); + } + + if (spw->sp_pwdp[0] == 'x' || spw->sp_pwdp[0] == '*' || spw->sp_pwdp[0] == '!') { + return HTTP_UNAUTHORIZED; + } + +//////// +/* Encrypt and compare shadow password */ + +// TODO : Valider qu'on a un user +// TODO : Valider qu'il y a un password (pas * ! rien) + char *encrypted; + const char *correct; + int rrr; + encrypted = crypt(pass, spw->sp_pwdp); + rrr = strcmp(encrypted, spw->sp_pwdp); + printf("compare pw : %s \n
", encrypted);; + printf("compare : %d \n
", rrr); + if (rrr!=0) { + return HTTP_UNAUTHORIZED; + } + + // now check supplemental groups + gid_t grouplist[16]; + int grouplistsize = 16; + int groupreturn; + groupreturn = getgrouplist("jlcyr", g, grouplist, &grouplistsize); + if (groupreturn != -1) { + printf( "OK liste des groupes (%d)
\r\n", grouplistsize); + for (i=0; i\r\n"); + return OK; + } +} \ No newline at end of file diff --git a/absec_etc.h b/absec_etc.h new file mode 100644 index 0000000..0fe66a8 --- /dev/null +++ b/absec_etc.h @@ -0,0 +1,10 @@ + +#define PAM_OK 0 +#define PAM_ERROR_START -1 +#define PAM_ERROR_INVALID_CRED -2 +#define PAM_ERROR_STOP -3 + +#define HTTP_UNAUTHORIZED 401 +#define OK 200 + +int check_user(char* user, char* pass); diff --git a/absec_ls.c b/absec_ls.c new file mode 100644 index 0000000..30e6319 --- /dev/null +++ b/absec_ls.c @@ -0,0 +1,92 @@ + +#include +#include +#include + +//////////////////////////////////////////////////////////////// +// define PAM callback function +char* perms_to_string(int perms) +{ + char* pstring; + pstring = malloc(11); + memcpy(pstring, "----------\0", 11); + + if (perms & 1) pstring[9] = 'x'; + if (perms & 2) pstring[8] = 'w'; + if (perms & 4) pstring[7] = 'r'; + if (perms & 8) pstring[6] = 'x'; + if (perms & 16) pstring[5] = 'w'; + if (perms & 32) pstring[4] = 'r'; + if (perms & 64) pstring[3] = 'x'; + if (perms & 128) pstring[2] = 'w'; + if (perms & 128) pstring[1] = 'r'; + + //printf("%s", pstring); + + return pstring; +} + +//////////////////////////////////////////////////////////////// +// define PAM callback function +int mysql_lookup()//request_rec *r, struct stat *fperm) +{ + MYSQL *conn; + MYSQL_RES *res; + MYSQL_ROW row; + + char *server = "localhost"; + char *user = "jlcyr"; + char *password = "password"; /* set me first */ + char *database = "absec"; + + conn = mysql_init(NULL); + + /* Connect to database */ + if (!mysql_real_connect(conn, server, + user, password, database, 0, NULL, 0)) { + printf("%s\n", mysql_error(conn)); + return(0); + } + + char query[256]; + sprintf(query, "select * from urls"); // where url='%s'", r->uri); + /* send SQL query */ + //if (mysql_query(conn, "show tables")) { + if (mysql_query(conn, query)) { + printf("%s\n", mysql_error(conn)); + return(-1); + } + + res = mysql_use_result(conn); + + /* output table name */ + printf("ABSEC permissions\n"); + int cnt = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + printf("%s\t%d\t%d\t%s \n", perms_to_string(atoi(row[3])), atoi(row[1]), atoi(row[2]), row[0] ); + + /*fperm->st_uid = atoi(row[1]); + fperm->st_gid = atoi(row[2]); + fperm->st_mode = atoi(row[3]);*/ + cnt = cnt + 1; + } + + /*if (cnt==0) { + sprintf(query, "insert into urls (url, uid, gid, perms) values ('%s', 0, 0, 0)", r->uri); + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + ap_rprintf(r, "Aucune donnee\n
"); + }*/ + + /* close connection */ + mysql_free_result(res); + mysql_close(conn); + return(0); +} + +void main(int argc, char** argv) +{ + mysql_lookup(); +} \ No newline at end of file diff --git a/absec_mysql.c b/absec_mysql.c new file mode 100644 index 0000000..cafa45c --- /dev/null +++ b/absec_mysql.c @@ -0,0 +1,64 @@ + + +#include +#include +#include "httpd.h" + +//////////////////////////////////////////////////////////////// +// +int mysql_lookup(request_rec *r, struct stat *fperm) +{ + MYSQL *conn; + MYSQL_RES *res; + MYSQL_ROW row; + + char *server = "localhost"; + char *user = "jlcyr"; + char *password = "password"; /* set me first */ + char *database = "absec"; + + conn = mysql_init(NULL); + + /* Connect to database */ + if (!mysql_real_connect(conn, server, + user, password, database, 0, NULL, 0)) { + printf("%s\n", mysql_error(conn)); + return(0); + } + + char query[256]; + sprintf(query, "select * from urls where url='%s'", r->uri); + /* send SQL query */ + //if (mysql_query(conn, "show tables")) { + if (mysql_query(conn, query)) { + printf("%s\n", mysql_error(conn)); + return(-1); + } + + res = mysql_use_result(conn); + + /* output table name */ + printf("MySQL data:\n
"); + int cnt = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + printf("%s %d %d %o \n
", row[0], atoi(row[1]), atoi(row[2]), atoi(row[3])); + fperm->st_uid = atoi(row[1]); + fperm->st_gid = atoi(row[2]); + fperm->st_mode = atoi(row[3]); + cnt = cnt + 1; + } + + if (cnt==0) { + sprintf(query, "insert into urls (url, uid, gid, perms) values ('%s', 0, 0, 0)", r->uri); + if (mysql_query(conn, query)) { + printf("%s\n", mysql_error(conn)); + return(0); + } + printf("Aucune donnee\n
"); + } + + /* close connection */ + mysql_free_result(res); + mysql_close(conn); + return(0); +} diff --git a/absec_mysql.h b/absec_mysql.h new file mode 100644 index 0000000..7669c4a --- /dev/null +++ b/absec_mysql.h @@ -0,0 +1,3 @@ + + +int mysql_lookup(request_rec *r, struct stat *fperm); diff --git a/absec_pam.c b/absec_pam.c new file mode 100644 index 0000000..ed3f2da --- /dev/null +++ b/absec_pam.c @@ -0,0 +1,66 @@ + + +#include "absec_pam.h" + +#include +#include + +//////////////////////////////////////////////////////////////// +/* Check user autentication against unix user/pass */ +/*static int check_autentication(request_rec *r) +{ + return 0; +}*/ + +//////////////////////////////////////////////////////////////// +/* Check check file perms */ +/*static int check_autorization(request_rec *r) +{ + return 0; +}*/ + +// Global var for passing fake response to PAM callback +struct pam_response *reply; + +//////////////////////////////////////////////////////////////// +// PAM response callback function +int converse(int n, const struct pam_message **msg, + struct pam_response **resp, void *data) +{ + // Return globally set response + *resp = reply; + return PAM_SUCCESS; +} + +//////////////////////////////////////////////////////////////// +// define PAM callback function +struct pam_conv conv = { converse, 0 }; + +int check_user(char* user, char* pass) { + //////// + // Connect to PAM to auth user + pam_handle_t * pamh = NULL; + int rret; + + if((rret = pam_start("httpd", user, &conv, &pamh)) != PAM_SUCCESS) { + return PAM_ERROR_START; + } + + // Set the PAM callback function response (would call for password) + reply = (struct pam_response *)malloc(sizeof(struct pam_response)); + reply[0].resp = strdup(pass); // password received in basic auth + reply[0].resp_retcode = 0; + + if((rret = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { + return PAM_ERROR_INVALID_CRED; + } + + if(pam_end(pamh, rret) != PAM_SUCCESS) { + } +} + +//////////////////////////////////////////////////////////////// +/*void main(int argc, char** argv) { + printf("absec_pam OK\n"); + exit(0); +}*/ \ No newline at end of file diff --git a/absec_pam.h b/absec_pam.h new file mode 100644 index 0000000..a057824 --- /dev/null +++ b/absec_pam.h @@ -0,0 +1,7 @@ + +#define PAM_OK 0 +#define PAM_ERROR_START -1 +#define PAM_ERROR_INVALID_CRED -2 +#define PAM_ERROR_STOP -3 + +int check_user(char* user, char* pass); diff --git a/compile.sh b/compile.sh index d1e8f90..0c67d8e 100755 --- a/compile.sh +++ b/compile.sh @@ -1,4 +1,84 @@ +#!/bin/bash +echo "----------------" +echo "Cleaning projets" +rm -fv *.o *.lo *.la *.slo + +echo "----------------" +echo "Compiling pam module" #/home/jlcyr/apache2/bin/apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i mod_absec.c -sudo apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i mod_absec.c +#gcc -fPIC -c -lpam -lpam_misc -o absec_pam.o absec_pam.c +#gcc absec_pam.c -lpam -lpam_misc -o absec_pam +gcc absec_pam.c -lpam -lpam_misc -c + +#ld -lpam -lpam_misc --shared -o absec_pam absec_pam.o +#./absec_pam +#exit + +echo "----------------" +echo "Compiling authen test with pam" +#/home/jlcyr/apache2/bin/apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i mod_absec.c +gcc test_authen.c absec_pam.o -lpam -lpam_misc -o test_authen +#gcc -o test_pam -lpam -lpam_misc absec_pam.o test_pam.c +#ld -o test_pam -lc --entry main test_pam.o +#ld -lpam -lpam_misc --shared -o test_pam absec_pam.o test_pam.o +echo "Running test" +./test_authen + +echo "----------------" +echo "Compiling etc module" +#/home/jlcyr/apache2/bin/apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i mod_absec.c +#gcc -fPIC -c -lpam -lpam_misc -o absec_pam.o absec_pam.c +#gcc absec_pam.c -lpam -lpam_misc -o absec_pam +gcc -I /usr/local/apache2/include -I /usr/include/apr-1.0 absec_etc.c -c + + +echo "----------------" +echo "Compiling authen test with etc" +#/home/jlcyr/apache2/bin/apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i mod_absec.c +gcc test_authen.c absec_etc.o -L/usr/lib/x86_64-linux-gnu -lapr-1 -lcrypt -o test_authen +#gcc -o test_pam -lpam -lpam_misc absec_pam.o test_pam.c +#ld -o test_pam -lc --entry main test_pam.o +#ld -lpam -lpam_misc --shared -o test_pam absec_pam.o test_pam.o +echo "Running test" +./test_authen + + +echo "----------------" +echo "Compiling mysql module" +#/home/jlcyr/apache2/bin/apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i mod_absec.c +#gcc -fPIC -c -lpam -lpam_misc -o absec_pam.o absec_pam.c +#gcc absec_pam.c -lpam -lpam_misc -o absec_pam +gcc absec_mysql.c `mysql_config --cflags --libs` -I/usr/include/apache2 -I/usr/include/apr-1.0 -c + +#ld -lpam -lpam_misc --shared -o absec_pam absec_pam.o +#./absec_pam +#exit + +echo "----------------" +echo "Compiling autho test with mysql" +#/home/jlcyr/apache2/bin/apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i mod_absec.c +gcc test_mysql.c absec_mysql.o -lpam -lpam_misc `mysql_config --cflags --libs` -o test_mysql +#gcc -o test_pam -lpam -lpam_misc absec_pam.o test_pam.c +#ld -o test_pam -lc --entry main test_pam.o +#ld -lpam -lpam_misc --shared -o test_pam absec_pam.o test_pam.o +echo "Running test" +./test_mysql + + +echo "----------------" +echo "Compiling absec tools" +echo "- absec_ls" +#/home/jlcyr/apache2/bin/apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i mod_absec.c +gcc absec_ls.c `mysql_config --cflags --libs` -o abls +#gcc -o test_pam -lpam -lpam_misc absec_pam.o test_pam.c +#ld -o test_pam -lc --entry main test_pam.o +#ld -lpam -lpam_misc --shared -o test_pam absec_pam.o test_pam.o +echo "Running test" +./abls +exit + +echo "----------------" +echo "Compiling absec module" +sudo apxs -lpam -lpam_misc `mysql_config --cflags --libs` -c -i absec_pam.o absec_mysql.o mod_absec.c #/home/jlcyr/apache2/bin/apachectl restart sudo /etc/init.d/apache2 restart diff --git a/mod_absec.c b/mod_absec.c index cccb0f7..749a8e8 100755 --- a/mod_absec.c +++ b/mod_absec.c @@ -58,100 +58,8 @@ #include #include -#include -#include - -#include - -//////////////////////////////////////////////////////////////// -/* Check user autentication against unix user/pass */ -static int check_autentication(request_rec *r) -{ - return 0; -} - -//////////////////////////////////////////////////////////////// -/* Check check file perms */ -static int check_autorization(request_rec *r) -{ - return 0; -} - -// Global var for passing fake response to PAM callback -struct pam_response *reply; - -//////////////////////////////////////////////////////////////// -// PAM response callback function -int converse(int n, const struct pam_message **msg, - struct pam_response **resp, void *data) -{ - // Return globally set response - *resp = reply; - return PAM_SUCCESS; -} - -//////////////////////////////////////////////////////////////// -// define PAM callback function -struct pam_conv conv = { converse, 0 }; - -//////////////////////////////////////////////////////////////// -// define PAM callback function -int mysql_lookup(request_rec *r, struct stat *fperm) -{ - MYSQL *conn; - MYSQL_RES *res; - MYSQL_ROW row; - - char *server = "localhost"; - char *user = "jlcyr"; - char *password = "password"; /* set me first */ - char *database = "absec"; - - conn = mysql_init(NULL); - - /* Connect to database */ - if (!mysql_real_connect(conn, server, - user, password, database, 0, NULL, 0)) { - ap_rprintf(r, "%s\n
", mysql_error(conn)); - return(0); - } - - char query[256]; - sprintf(query, "select * from urls where url='%s'", r->uri); - /* send SQL query */ - //if (mysql_query(conn, "show tables")) { - if (mysql_query(conn, query)) { - ap_rprintf(r, "%s\n
", mysql_error(conn)); - return(-1); - } - - res = mysql_use_result(conn); - - /* output table name */ - ap_rprintf(r, "MySQL data:\n
"); - int cnt = 0; - while ((row = mysql_fetch_row(res)) != NULL) { - ap_rprintf(r, "%s %d %d %o \n
", row[0], atoi(row[1]), atoi(row[2]), atoi(row[3])); - fperm->st_uid = atoi(row[1]); - fperm->st_gid = atoi(row[2]); - fperm->st_mode = atoi(row[3]); - cnt = cnt + 1; - } - - if (cnt==0) { - sprintf(query, "insert into urls (url, uid, gid, perms) values ('%s', 0, 0, 0)", r->uri); - if (mysql_query(conn, query)) { - ap_rprintf(r, "%s\n
", mysql_error(conn)); - return(0); - } - ap_rprintf(r, "Aucune donnee\n
"); - } - - /* close connection */ - mysql_free_result(res); - mysql_close(conn); - return(0); -} +#include "absec_pam.h" +#include "absec_mysql.h" //////////////////////////////////////////////////////////////// /* Main routine - called after request processing */ @@ -206,8 +114,8 @@ static int absec_handler_first(request_rec *r) /* should include */ struct stat fperm; int status; - //status = stat(r->filename, &fperm); - status = mysql_lookup(r, &fperm); + status = stat(r->filename, &fperm); + //status = mysql_lookup(r, &fperm); //ap_rprintf(r, "Result mysql: %d
\r\n", ); if (status==-1) { ap_rprintf(r, "stat erreur %d", errno); @@ -264,23 +172,11 @@ static int absec_handler_first(request_rec *r) //ap_rprintf(r, "Headers Authorization: %s \n
", auth64); //ap_rprintf(r, "User/Pass: %s/%s \n
", user, pass); - //////// - // Connect to PAM to auth user - pam_handle_t * pamh = NULL; - int rret; - - if((rret = pam_start("httpd", user/*pw->pw_name*/, &conv, &pamh)) != PAM_SUCCESS) { + int pam_result = pam_check_user(user, pass); + if ( (pam_result==PAM_ERROR_START) || (pam_result==PAM_ERROR_STOP) ) { return HTTP_INTERNAL_SERVER_ERROR; - printf("Pam start failed\n"); - exit(0); } - - // Set the PAM callback function response (would call for password) - reply = (struct pam_response *)malloc(sizeof(struct pam_response)); - reply[0].resp = strdup(pass); // password received in basic auth - reply[0].resp_retcode = 0; - - if((rret = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { + if (pam_result==PAM_ERROR_INVALID_CRED) { r->content_type = "text/html"; apr_table_setn(r->err_headers_out, (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" @@ -288,15 +184,6 @@ static int absec_handler_first(request_rec *r) apr_pstrcat(r->pool, "Basic realm=\"USER/PASS INVALIDE ", ap_auth_name(r), "\"", NULL)); return HTTP_UNAUTHORIZED; - printf("User auth failed\n"); - exit(0); - } - - if(pam_end(pamh, rret) != PAM_SUCCESS) { - //perror("pam_end"); - pamh = NULL; - return HTTP_INTERNAL_SERVER_ERROR; - exit(1); } //////// diff --git a/mod_absec_a.c b/mod_absec_a.c new file mode 100644 index 0000000..d43e184 --- /dev/null +++ b/mod_absec_a.c @@ -0,0 +1,378 @@ +/* +** mod_absec.c -- Apache sample absec module +** [Autogenerated via ``apxs -n absec -g''] +** +** To play with this sample module first compile it into a +** DSO file and install it into Apache's modules directory +** by running: +** +** $ apxs -c -i mod_absec.c +** +** Then activate it in Apache's httpd.conf file for instance +** for the URL /absec in as follows: +** +** # httpd.conf +** LoadModule absec_module modules/mod_absec.so +** +** SetHandler absec +** +** +** Then after restarting Apache via +** +** $ apachectl restart +** +*/ + +/* + TEST URL + http://10.211.55.15/absec?joe=blow + + INFORMATION SOURCES + + https://apr.apache.org/docs/apr/1.5/group__apr__strings.html + https://apr.apache.org/docs/apr-util/1.6/files.html + + https://httpd.apache.org/docs/2.4/developer/modguide.html + http://www.ziviani.net/2011/how-to-create-an-apache-module + + https://en.wikipedia.org/wiki/Basic_access_authentication + +*/ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "ap_config.h" +#include "apr_base64.h" +#include "apr_strings.h" +#include "apr_portable.h" +#include "apr_user.h" + +#include +#include +#include +#include +#include "apr_want.h" + +#include +#include + +#include + +//////////////////////////////////////////////////////////////// +/* Check user autentication against unix user/pass */ +static int check_autentication(request_rec *r) +{ + return 0; +} + +//////////////////////////////////////////////////////////////// +/* Check check file perms */ +static int check_autorization(request_rec *r) +{ + return 0; +} + +//////////////////////////////////////////////////////////////// +// define PAM callback function +int mysql_lookup(request_rec *r, struct stat *fperm) +{ + MYSQL *conn; + MYSQL_RES *res; + MYSQL_ROW row; + + char *server = "localhost"; + char *user = "jlcyr"; + char *password = "password"; /* set me first */ + char *database = "absec"; + + conn = mysql_init(NULL); + + /* Connect to database */ + if (!mysql_real_connect(conn, server, + user, password, database, 0, NULL, 0)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + + char query[256]; + sprintf(query, "select * from urls where url='%s'", r->uri); + /* send SQL query */ + //if (mysql_query(conn, "show tables")) { + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(-1); + } + + res = mysql_use_result(conn); + + /* output table name */ + ap_rprintf(r, "MySQL data:\n
"); + int cnt = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + ap_rprintf(r, "%s %d %d %o \n
", row[0], atoi(row[1]), atoi(row[2]), atoi(row[3])); + fperm->st_uid = atoi(row[1]); + fperm->st_gid = atoi(row[2]); + fperm->st_mode = atoi(row[3]); + cnt = cnt + 1; + } + + if (cnt==0) { + sprintf(query, "insert into urls (url, uid, gid, perms) values ('%s', 0, 0, 0)", r->uri); + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + ap_rprintf(r, "Aucune donnee\n
"); + } + + /* close connection */ + mysql_free_result(res); + mysql_close(conn); + return(0); +} + +//////////////////////////////////////////////////////////////// +/* Main routine */ +static int absec_handler_last(request_rec *r) +{ + // Is this module really called? + if (strcmp(r->handler, "absec")) { + return DECLINED; + } + r->content_type = "text/html"; + //ap_rprintf(r, "The sample page from mod_absec.c %s \n
", r->args); + ap_rprintf(r, "After Url: %s from %s \n
", r->filename, r->uri); + return (OK); +} + +//////////////////////////////////////////////////////////////// +/* Main routine */ +static int absec_handler_first(request_rec *r) +{ + // Is this module really called? + if (strcmp(r->handler, "absec")) { + return DECLINED; + } + +//////// +/* http method validate the perm asked (r/w vs get/post,put) */ + ap_rprintf(r, "Method: %s
\r\n", r->method); + int permmask = 0; + if (strcmp(r->method,"GET")==0) permmask=0444; // r + if (strcmp(r->method,"PUT")==0) permmask=0222; // w + if (strcmp(r->method,"POST")==0) permmask=0222; // w + if (strcmp(r->method,"DELETE")==0) permmask=0111; // x + +//////// +/* check file permission on filesystem */ +/* should include */ + struct stat fperm; + int status; + //status = stat(r->filename, &fperm); + status = mysql_lookup(r, &fperm); + //ap_rprintf(r, "Result mysql: %d
\r\n", ); + if (status==-1) { + ap_rprintf(r, "stat erreur %d", errno); + return (OK); + } + //ap_rprintf(r, "File perms %o, owner %d, group %d (status %d)
\r\n", fperm.st_mode, fperm.st_uid, fperm.st_gid, status); + + /* check if any permission (ogw) match method (get r, put/post w, delete x) */ + if ((fperm.st_mode & permmask)==0) { + /* no permission match, return don't even have to check user perms */ + //ap_rprintf(r, "Aucune permission pour la methode %s (%o, %o)", r->method, fperm.st_mode, permmask); + return (OK); + } + + // If file is world accessible for asked method return content + // TODO : if put/post/delete, must check BEFORE ACTION not AFTER!!! + if (fperm.st_mode & 0x7 & permmask) { + /* if so, return, no need to check user perms */ + //ap_rprintf(r, "Fichier public
\r\n"); + return (DECLINED); + } + +//////// +/* Check if we have a basic auth user */ + const char* auth64p; + // Check if we have an auth header + auth64p = apr_table_get(r->headers_in,"Authorization"); + + // If no basic auth, ask for one + if (auth64p==NULL) { + r->content_type = "text/html"; + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + +//////// +/* Retrieve user/pass from http basic auth header */ + // Get the basic auth base64 string and decode it + // Start at char 6 to skip 'Basic ' + char *auth64; + auth64 = apr_pstrdup(r->pool, auth64p+6); + char *auth; + auth = apr_pcalloc(r->pool, 64); + apr_base64_decode(auth, auth64); + + // Validate user/pass against unix cred + char *user; + char *pass; + user = apr_strtok(auth, ":", &pass); + + r->content_type = "text/html"; + //ap_rprintf(r, "The sample page from mod_absec.c %s \n
", r->args); + ap_rprintf(r, "Url: %s from %s \n
", r->filename, r->uri); + //ap_rprintf(r, "Headers Authorization: %s \n
", auth64); + //ap_rprintf(r, "User/Pass: %s/%s \n
", user, pass); + +//////// +// Get UID, GIDs for the user +/* Working example, but just UID not PW */ + apr_status_t ret; + apr_uid_t i; + apr_gid_t g; + ret = apr_uid_get ( &i, &g, user, r->pool ); + ap_rprintf(r, "Result2: G:%d, I:%d \n
", g,i); + +//////// +/* Retrieve PW from /etc/passwd */ +/* Should include */ + struct passwd *pw; + if((pw = getpwnam(user)) == NULL) + { + ap_rprintf(r, "NULL \n
"); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + else + { + ap_rprintf(r, "Unix PW : %s \n
", pw->pw_passwd); + } + +//////// +/* Retrieve PW from /etc/shadow */ +/* Should include */ + struct spwd *spw; + errno = 0; + if((spw = getspnam(user)) == NULL) + { + ap_rprintf(r, "NULL %d\n
", errno); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + else + { + ap_rprintf(r, "Shadow PW : %s \n
", spw->sp_pwdp); + } + + if (spw->sp_pwdp[0] == 'x' || spw->sp_pwdp[0] == '*' || spw->sp_pwdp[0] == '!') { + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + +//////// +/* Encrypt and compare shadow password */ + +// TODO : Valider qu'on a un user +// TODO : Valider qu'il y a un password (pas * ! rien) + char *encrypted; + const char *correct; + int rrr; + encrypted = crypt(pass, spw->sp_pwdp); + rrr = strcmp(encrypted, spw->sp_pwdp); + ap_rprintf(r, "compare pw : %s \n
", encrypted);; + ap_rprintf(r, "compare : %d \n
", rrr); + if (rrr!=0) { + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + + + // If file is user readable and user match return content + if ((fperm.st_uid==i) && (fperm.st_mode & 0700 & permmask)) { + ap_rprintf(r, "Fichier propriétaire
\r\n"); + return (DECLINED); + } + + // If file is group readable and primary group match return content + if ((fperm.st_gid==g) && (fperm.st_mode & 0070 & permmask)) { + ap_rprintf(r, "Fichier groupe
\r\n"); + return (DECLINED); + } + + // now check supplemental groups + //ap_rprintf(r, "Fichier propriétaire %d %d %o %o
\r\n", fperm.st_uid, i, fperm.st_mode, 0400); + gid_t grouplist[16]; + int grouplistsize = 16; + int *groupreturn; + groupreturn = getgrouplist("jlcyr", g, grouplist, &grouplistsize); + if (groupreturn != -1) { + ap_rprintf(r, "OK liste des groupes (%d)
\r\n", grouplistsize); + for (i=0; i\r\n"); + return (DECLINED); + } + } + } else { + ap_rprintf(r, "Erreur
\r\n"); + return OK; + } + + // else decline + ap_rprintf(r, "Aucuns droits de voir le fichier
\r\n"); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + return OK; + +} + +//////////////////////////////////////////////////////////////// +static void absec_register_hooks(apr_pool_t *p) +{ + //ap_hook_handler(absec_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(absec_handler_last, NULL, NULL, APR_HOOK_LAST); + ap_hook_handler(absec_handler_first, NULL, NULL, APR_HOOK_FIRST); +} + +//////////////////////////////////////////////////////////////// +/* Dispatch list for API hooks */ +module AP_MODULE_DECLARE_DATA absec_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + NULL, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + NULL, /* table of config file commands */ + absec_register_hooks /* register hooks */ +}; + diff --git a/mod_absec_b.c b/mod_absec_b.c new file mode 100644 index 0000000..d43e184 --- /dev/null +++ b/mod_absec_b.c @@ -0,0 +1,378 @@ +/* +** mod_absec.c -- Apache sample absec module +** [Autogenerated via ``apxs -n absec -g''] +** +** To play with this sample module first compile it into a +** DSO file and install it into Apache's modules directory +** by running: +** +** $ apxs -c -i mod_absec.c +** +** Then activate it in Apache's httpd.conf file for instance +** for the URL /absec in as follows: +** +** # httpd.conf +** LoadModule absec_module modules/mod_absec.so +** +** SetHandler absec +** +** +** Then after restarting Apache via +** +** $ apachectl restart +** +*/ + +/* + TEST URL + http://10.211.55.15/absec?joe=blow + + INFORMATION SOURCES + + https://apr.apache.org/docs/apr/1.5/group__apr__strings.html + https://apr.apache.org/docs/apr-util/1.6/files.html + + https://httpd.apache.org/docs/2.4/developer/modguide.html + http://www.ziviani.net/2011/how-to-create-an-apache-module + + https://en.wikipedia.org/wiki/Basic_access_authentication + +*/ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "ap_config.h" +#include "apr_base64.h" +#include "apr_strings.h" +#include "apr_portable.h" +#include "apr_user.h" + +#include +#include +#include +#include +#include "apr_want.h" + +#include +#include + +#include + +//////////////////////////////////////////////////////////////// +/* Check user autentication against unix user/pass */ +static int check_autentication(request_rec *r) +{ + return 0; +} + +//////////////////////////////////////////////////////////////// +/* Check check file perms */ +static int check_autorization(request_rec *r) +{ + return 0; +} + +//////////////////////////////////////////////////////////////// +// define PAM callback function +int mysql_lookup(request_rec *r, struct stat *fperm) +{ + MYSQL *conn; + MYSQL_RES *res; + MYSQL_ROW row; + + char *server = "localhost"; + char *user = "jlcyr"; + char *password = "password"; /* set me first */ + char *database = "absec"; + + conn = mysql_init(NULL); + + /* Connect to database */ + if (!mysql_real_connect(conn, server, + user, password, database, 0, NULL, 0)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + + char query[256]; + sprintf(query, "select * from urls where url='%s'", r->uri); + /* send SQL query */ + //if (mysql_query(conn, "show tables")) { + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(-1); + } + + res = mysql_use_result(conn); + + /* output table name */ + ap_rprintf(r, "MySQL data:\n
"); + int cnt = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + ap_rprintf(r, "%s %d %d %o \n
", row[0], atoi(row[1]), atoi(row[2]), atoi(row[3])); + fperm->st_uid = atoi(row[1]); + fperm->st_gid = atoi(row[2]); + fperm->st_mode = atoi(row[3]); + cnt = cnt + 1; + } + + if (cnt==0) { + sprintf(query, "insert into urls (url, uid, gid, perms) values ('%s', 0, 0, 0)", r->uri); + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + ap_rprintf(r, "Aucune donnee\n
"); + } + + /* close connection */ + mysql_free_result(res); + mysql_close(conn); + return(0); +} + +//////////////////////////////////////////////////////////////// +/* Main routine */ +static int absec_handler_last(request_rec *r) +{ + // Is this module really called? + if (strcmp(r->handler, "absec")) { + return DECLINED; + } + r->content_type = "text/html"; + //ap_rprintf(r, "The sample page from mod_absec.c %s \n
", r->args); + ap_rprintf(r, "After Url: %s from %s \n
", r->filename, r->uri); + return (OK); +} + +//////////////////////////////////////////////////////////////// +/* Main routine */ +static int absec_handler_first(request_rec *r) +{ + // Is this module really called? + if (strcmp(r->handler, "absec")) { + return DECLINED; + } + +//////// +/* http method validate the perm asked (r/w vs get/post,put) */ + ap_rprintf(r, "Method: %s
\r\n", r->method); + int permmask = 0; + if (strcmp(r->method,"GET")==0) permmask=0444; // r + if (strcmp(r->method,"PUT")==0) permmask=0222; // w + if (strcmp(r->method,"POST")==0) permmask=0222; // w + if (strcmp(r->method,"DELETE")==0) permmask=0111; // x + +//////// +/* check file permission on filesystem */ +/* should include */ + struct stat fperm; + int status; + //status = stat(r->filename, &fperm); + status = mysql_lookup(r, &fperm); + //ap_rprintf(r, "Result mysql: %d
\r\n", ); + if (status==-1) { + ap_rprintf(r, "stat erreur %d", errno); + return (OK); + } + //ap_rprintf(r, "File perms %o, owner %d, group %d (status %d)
\r\n", fperm.st_mode, fperm.st_uid, fperm.st_gid, status); + + /* check if any permission (ogw) match method (get r, put/post w, delete x) */ + if ((fperm.st_mode & permmask)==0) { + /* no permission match, return don't even have to check user perms */ + //ap_rprintf(r, "Aucune permission pour la methode %s (%o, %o)", r->method, fperm.st_mode, permmask); + return (OK); + } + + // If file is world accessible for asked method return content + // TODO : if put/post/delete, must check BEFORE ACTION not AFTER!!! + if (fperm.st_mode & 0x7 & permmask) { + /* if so, return, no need to check user perms */ + //ap_rprintf(r, "Fichier public
\r\n"); + return (DECLINED); + } + +//////// +/* Check if we have a basic auth user */ + const char* auth64p; + // Check if we have an auth header + auth64p = apr_table_get(r->headers_in,"Authorization"); + + // If no basic auth, ask for one + if (auth64p==NULL) { + r->content_type = "text/html"; + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + +//////// +/* Retrieve user/pass from http basic auth header */ + // Get the basic auth base64 string and decode it + // Start at char 6 to skip 'Basic ' + char *auth64; + auth64 = apr_pstrdup(r->pool, auth64p+6); + char *auth; + auth = apr_pcalloc(r->pool, 64); + apr_base64_decode(auth, auth64); + + // Validate user/pass against unix cred + char *user; + char *pass; + user = apr_strtok(auth, ":", &pass); + + r->content_type = "text/html"; + //ap_rprintf(r, "The sample page from mod_absec.c %s \n
", r->args); + ap_rprintf(r, "Url: %s from %s \n
", r->filename, r->uri); + //ap_rprintf(r, "Headers Authorization: %s \n
", auth64); + //ap_rprintf(r, "User/Pass: %s/%s \n
", user, pass); + +//////// +// Get UID, GIDs for the user +/* Working example, but just UID not PW */ + apr_status_t ret; + apr_uid_t i; + apr_gid_t g; + ret = apr_uid_get ( &i, &g, user, r->pool ); + ap_rprintf(r, "Result2: G:%d, I:%d \n
", g,i); + +//////// +/* Retrieve PW from /etc/passwd */ +/* Should include */ + struct passwd *pw; + if((pw = getpwnam(user)) == NULL) + { + ap_rprintf(r, "NULL \n
"); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + else + { + ap_rprintf(r, "Unix PW : %s \n
", pw->pw_passwd); + } + +//////// +/* Retrieve PW from /etc/shadow */ +/* Should include */ + struct spwd *spw; + errno = 0; + if((spw = getspnam(user)) == NULL) + { + ap_rprintf(r, "NULL %d\n
", errno); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + else + { + ap_rprintf(r, "Shadow PW : %s \n
", spw->sp_pwdp); + } + + if (spw->sp_pwdp[0] == 'x' || spw->sp_pwdp[0] == '*' || spw->sp_pwdp[0] == '!') { + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + +//////// +/* Encrypt and compare shadow password */ + +// TODO : Valider qu'on a un user +// TODO : Valider qu'il y a un password (pas * ! rien) + char *encrypted; + const char *correct; + int rrr; + encrypted = crypt(pass, spw->sp_pwdp); + rrr = strcmp(encrypted, spw->sp_pwdp); + ap_rprintf(r, "compare pw : %s \n
", encrypted);; + ap_rprintf(r, "compare : %d \n
", rrr); + if (rrr!=0) { + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + + + // If file is user readable and user match return content + if ((fperm.st_uid==i) && (fperm.st_mode & 0700 & permmask)) { + ap_rprintf(r, "Fichier propriétaire
\r\n"); + return (DECLINED); + } + + // If file is group readable and primary group match return content + if ((fperm.st_gid==g) && (fperm.st_mode & 0070 & permmask)) { + ap_rprintf(r, "Fichier groupe
\r\n"); + return (DECLINED); + } + + // now check supplemental groups + //ap_rprintf(r, "Fichier propriétaire %d %d %o %o
\r\n", fperm.st_uid, i, fperm.st_mode, 0400); + gid_t grouplist[16]; + int grouplistsize = 16; + int *groupreturn; + groupreturn = getgrouplist("jlcyr", g, grouplist, &grouplistsize); + if (groupreturn != -1) { + ap_rprintf(r, "OK liste des groupes (%d)
\r\n", grouplistsize); + for (i=0; i\r\n"); + return (DECLINED); + } + } + } else { + ap_rprintf(r, "Erreur
\r\n"); + return OK; + } + + // else decline + ap_rprintf(r, "Aucuns droits de voir le fichier
\r\n"); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + return OK; + +} + +//////////////////////////////////////////////////////////////// +static void absec_register_hooks(apr_pool_t *p) +{ + //ap_hook_handler(absec_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(absec_handler_last, NULL, NULL, APR_HOOK_LAST); + ap_hook_handler(absec_handler_first, NULL, NULL, APR_HOOK_FIRST); +} + +//////////////////////////////////////////////////////////////// +/* Dispatch list for API hooks */ +module AP_MODULE_DECLARE_DATA absec_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + NULL, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + NULL, /* table of config file commands */ + absec_register_hooks /* register hooks */ +}; + diff --git a/mod_absec_c.c b/mod_absec_c.c new file mode 100644 index 0000000..cccb0f7 --- /dev/null +++ b/mod_absec_c.c @@ -0,0 +1,388 @@ +/* +** mod_absec.c -- Apache absec module +** [base Autogenerated via ``apxs -n absec -g''] +** +** To play with this sample module first compile it into a +** DSO file and install it into Apache's modules directory +** by running: +** +** $ apxs -lpam -lpam_misc -c -i mod_absec.c +** +** Then activate it in Apache's httpd.conf file for instance +** for the URL /absec in as follows: +** +** # httpd.conf +** LoadModule absec_module modules/mod_absec.so +** +** SetHandler absec +** +** +** Then after restarting Apache via +** +** $ apachectl restart +** +*/ + +/* + TEST URL + http://10.211.55.15/absec/ + + INFORMATION SOURCES + + https://apr.apache.org/docs/apr/1.5/group__apr__strings.html + https://apr.apache.org/docs/apr-util/1.6/files.html + + https://httpd.apache.org/docs/2.4/developer/modguide.html + http://www.ziviani.net/2011/how-to-create-an-apache-module + + https://en.wikipedia.org/wiki/Basic_access_authentication + +*/ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "ap_config.h" +#include "apr_base64.h" +#include "apr_strings.h" +#include "apr_portable.h" +#include "apr_user.h" + +#include +#include +#include +#include +#include "apr_want.h" + +#include +#include + +#include +#include + +#include + +//////////////////////////////////////////////////////////////// +/* Check user autentication against unix user/pass */ +static int check_autentication(request_rec *r) +{ + return 0; +} + +//////////////////////////////////////////////////////////////// +/* Check check file perms */ +static int check_autorization(request_rec *r) +{ + return 0; +} + +// Global var for passing fake response to PAM callback +struct pam_response *reply; + +//////////////////////////////////////////////////////////////// +// PAM response callback function +int converse(int n, const struct pam_message **msg, + struct pam_response **resp, void *data) +{ + // Return globally set response + *resp = reply; + return PAM_SUCCESS; +} + +//////////////////////////////////////////////////////////////// +// define PAM callback function +struct pam_conv conv = { converse, 0 }; + +//////////////////////////////////////////////////////////////// +// define PAM callback function +int mysql_lookup(request_rec *r, struct stat *fperm) +{ + MYSQL *conn; + MYSQL_RES *res; + MYSQL_ROW row; + + char *server = "localhost"; + char *user = "jlcyr"; + char *password = "password"; /* set me first */ + char *database = "absec"; + + conn = mysql_init(NULL); + + /* Connect to database */ + if (!mysql_real_connect(conn, server, + user, password, database, 0, NULL, 0)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + + char query[256]; + sprintf(query, "select * from urls where url='%s'", r->uri); + /* send SQL query */ + //if (mysql_query(conn, "show tables")) { + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(-1); + } + + res = mysql_use_result(conn); + + /* output table name */ + ap_rprintf(r, "MySQL data:\n
"); + int cnt = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + ap_rprintf(r, "%s %d %d %o \n
", row[0], atoi(row[1]), atoi(row[2]), atoi(row[3])); + fperm->st_uid = atoi(row[1]); + fperm->st_gid = atoi(row[2]); + fperm->st_mode = atoi(row[3]); + cnt = cnt + 1; + } + + if (cnt==0) { + sprintf(query, "insert into urls (url, uid, gid, perms) values ('%s', 0, 0, 0)", r->uri); + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + ap_rprintf(r, "Aucune donnee\n
"); + } + + /* close connection */ + mysql_free_result(res); + mysql_close(conn); + return(0); +} + +//////////////////////////////////////////////////////////////// +/* Main routine - called after request processing */ +static int absec_handler_last(request_rec *r) +{ + // Is this module really called? + if (strcmp(r->handler, "absec")) { + return DECLINED; + } + + //////// + /* http method validate the perm asked (r/w vs get/post,put) */ + ap_rprintf(r, "After Method: %s
\r\n", r->method); + int permmask = 0; + if (strcmp(r->method,"GET")!=0) + { + // Not a GET, it's too late to do anything + ap_rprintf(r, "Not a GET post treatement is too late!\n
"); + return (DECLINED); + } + + r->content_type = "text/html"; + //ap_rprintf(r, "The sample page from mod_absec.c %s \n
", r->args); + ap_rprintf(r, "After GET Url: %s from %s \n
", r->filename, r->uri); + return (DECLINED); +} + +//////////////////////////////////////////////////////////////// +/* Main routine - called before request processing */ +static int absec_handler_first(request_rec *r) +{ + ap_rprintf(r, "Before Method: %s
\r\n", r->method); + // Is this module really called? + /*if (strcmp(r->handler, "absec")) { + ap_rprintf(r, "DECLINED
\r\n"); + return DECLINED; + }*/ + + + //////// + /* http method validate the perm asked (r/w vs get/post,put) */ + ap_rprintf(r, "Before Method: %s
\r\n", r->method); + + int permmask = 0; + if (strcmp(r->method,"GET")==0) permmask=0444; // r + if (strcmp(r->method,"PUT")==0) permmask=0222; // w + if (strcmp(r->method,"POST")==0) permmask=0222; // w + if (strcmp(r->method,"DELETE")==0) permmask=0111; // x + + //////// + /* check file permission on filesystem */ + /* should include */ + struct stat fperm; + int status; + //status = stat(r->filename, &fperm); + status = mysql_lookup(r, &fperm); + //ap_rprintf(r, "Result mysql: %d
\r\n", ); + if (status==-1) { + ap_rprintf(r, "stat erreur %d", errno); + return (OK); + } + //ap_rprintf(r, "File perms %o, owner %d, group %d (status %d)
\r\n", fperm.st_mode, fperm.st_uid, fperm.st_gid, status); + + /* check if any permission (ogw) match method (get r, put/post w, delete x) */ + if ((fperm.st_mode & permmask)==0) { + /* no permission match, return don't even have to check user perms */ + ap_rprintf(r, "Aucune permission pour la methode %s (%o, %o)", r->method, fperm.st_mode, permmask); + return (OK); + } + + // If file is world accessible for asked method return content + if (fperm.st_mode & 0x7 & permmask) { + /* if so, return, no need to check user perms */ + //ap_rprintf(r, "Fichier public
\r\n"); + return (DECLINED); + } + + //////// + /* Check if we have a basic auth user */ + const char* auth64p; + // Check if we have an auth header + auth64p = apr_table_get(r->headers_in,"Authorization"); + + // If no basic auth, ask for one + if (auth64p==NULL) { + r->content_type = "text/html"; + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"PAS DE USER ", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + + //////// + /* Retrieve user/pass from http basic auth header */ + // Get the basic auth base64 string and decode it + // Start at char 6 to skip 'Basic ' + char *auth64; + auth64 = apr_pstrdup(r->pool, auth64p+6); + char *auth; + auth = apr_pcalloc(r->pool, 64); + apr_base64_decode(auth, auth64); + char *user; + char *pass; + user = apr_strtok(auth, ":", &pass); + + r->content_type = "text/html"; + ap_rprintf(r, "Url: %s from %s \n
", r->filename, r->uri); + //ap_rprintf(r, "Headers Authorization: %s \n
", auth64); + //ap_rprintf(r, "User/Pass: %s/%s \n
", user, pass); + + //////// + // Connect to PAM to auth user + pam_handle_t * pamh = NULL; + int rret; + + if((rret = pam_start("httpd", user/*pw->pw_name*/, &conv, &pamh)) != PAM_SUCCESS) { + return HTTP_INTERNAL_SERVER_ERROR; + printf("Pam start failed\n"); + exit(0); + } + + // Set the PAM callback function response (would call for password) + reply = (struct pam_response *)malloc(sizeof(struct pam_response)); + reply[0].resp = strdup(pass); // password received in basic auth + reply[0].resp_retcode = 0; + + if((rret = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { + r->content_type = "text/html"; + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"USER/PASS INVALIDE ", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + printf("User auth failed\n"); + exit(0); + } + + if(pam_end(pamh, rret) != PAM_SUCCESS) { + //perror("pam_end"); + pamh = NULL; + return HTTP_INTERNAL_SERVER_ERROR; + exit(1); + } + + //////// + // Continue checking permission + + //////// + /* Retrieve user details from /etc/passwd to get uid and primary group */ + /* Should include */ + struct passwd *pw; + if((pw = getpwnam(user)) == NULL) + { + // Should never happend as already verified with PAM + ap_rprintf(r, "NULL \n
"); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"USER INCONNU ", ap_auth_name(r), + "\"", NULL)); + // User cannot be found, unauthorized + return HTTP_UNAUTHORIZED; + } + + // If file is user readable and user match return content + if ((fperm.st_uid==pw->pw_uid) && (fperm.st_mode & 0700 & permmask)) { + ap_rprintf(r, "Fichier propriétaire
\r\n"); + return (DECLINED); + } + + // If file is group readable and primary group match return content + if ((fperm.st_gid==pw->pw_gid) && (fperm.st_mode & 0070 & permmask)) { + ap_rprintf(r, "Fichier groupe
\r\n"); + return (DECLINED); + } + + //////// + /* Check supplemental groups */ + /* Should include */ + //ap_rprintf(r, "Fichier propriétaire %d %d %o %o
\r\n", fperm.st_uid, i, fperm.st_mode, 0400); + gid_t grouplist[16]; + int grouplistsize = 16; + int groupreturn; + groupreturn = getgrouplist(user, pw->pw_gid, grouplist, &grouplistsize); + if (groupreturn >= 0) { + ap_rprintf(r, "OK liste des groupes (%d)
\r\n", grouplistsize); + for (int i=0; i\r\n"); + return (DECLINED); + } + } + } + + // else decline request + ap_rprintf(r, "Aucuns droits de voir le fichier
\r\n"); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"NON AUTHORISE", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + +} + +//////////////////////////////////////////////////////////////// +static void absec_register_hooks(apr_pool_t *p) +{ + //ap_hook_handler(absec_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(absec_handler_last, NULL, NULL, APR_HOOK_LAST); + ap_hook_handler(absec_handler_first, NULL, NULL, APR_HOOK_FIRST); + + // should use HOOK FIXUP + + // should use FILTER +} + +//////////////////////////////////////////////////////////////// +/* Dispatch list for API hooks */ +module AP_MODULE_DECLARE_DATA absec_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + NULL, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + NULL, /* table of config file commands */ + absec_register_hooks /* register hooks */ +}; + diff --git a/mod_absec_d.c b/mod_absec_d.c new file mode 100644 index 0000000..cccb0f7 --- /dev/null +++ b/mod_absec_d.c @@ -0,0 +1,388 @@ +/* +** mod_absec.c -- Apache absec module +** [base Autogenerated via ``apxs -n absec -g''] +** +** To play with this sample module first compile it into a +** DSO file and install it into Apache's modules directory +** by running: +** +** $ apxs -lpam -lpam_misc -c -i mod_absec.c +** +** Then activate it in Apache's httpd.conf file for instance +** for the URL /absec in as follows: +** +** # httpd.conf +** LoadModule absec_module modules/mod_absec.so +** +** SetHandler absec +** +** +** Then after restarting Apache via +** +** $ apachectl restart +** +*/ + +/* + TEST URL + http://10.211.55.15/absec/ + + INFORMATION SOURCES + + https://apr.apache.org/docs/apr/1.5/group__apr__strings.html + https://apr.apache.org/docs/apr-util/1.6/files.html + + https://httpd.apache.org/docs/2.4/developer/modguide.html + http://www.ziviani.net/2011/how-to-create-an-apache-module + + https://en.wikipedia.org/wiki/Basic_access_authentication + +*/ + +#include "httpd.h" +#include "http_config.h" +#include "http_core.h" +#include "http_protocol.h" +#include "ap_config.h" +#include "apr_base64.h" +#include "apr_strings.h" +#include "apr_portable.h" +#include "apr_user.h" + +#include +#include +#include +#include +#include "apr_want.h" + +#include +#include + +#include +#include + +#include + +//////////////////////////////////////////////////////////////// +/* Check user autentication against unix user/pass */ +static int check_autentication(request_rec *r) +{ + return 0; +} + +//////////////////////////////////////////////////////////////// +/* Check check file perms */ +static int check_autorization(request_rec *r) +{ + return 0; +} + +// Global var for passing fake response to PAM callback +struct pam_response *reply; + +//////////////////////////////////////////////////////////////// +// PAM response callback function +int converse(int n, const struct pam_message **msg, + struct pam_response **resp, void *data) +{ + // Return globally set response + *resp = reply; + return PAM_SUCCESS; +} + +//////////////////////////////////////////////////////////////// +// define PAM callback function +struct pam_conv conv = { converse, 0 }; + +//////////////////////////////////////////////////////////////// +// define PAM callback function +int mysql_lookup(request_rec *r, struct stat *fperm) +{ + MYSQL *conn; + MYSQL_RES *res; + MYSQL_ROW row; + + char *server = "localhost"; + char *user = "jlcyr"; + char *password = "password"; /* set me first */ + char *database = "absec"; + + conn = mysql_init(NULL); + + /* Connect to database */ + if (!mysql_real_connect(conn, server, + user, password, database, 0, NULL, 0)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + + char query[256]; + sprintf(query, "select * from urls where url='%s'", r->uri); + /* send SQL query */ + //if (mysql_query(conn, "show tables")) { + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(-1); + } + + res = mysql_use_result(conn); + + /* output table name */ + ap_rprintf(r, "MySQL data:\n
"); + int cnt = 0; + while ((row = mysql_fetch_row(res)) != NULL) { + ap_rprintf(r, "%s %d %d %o \n
", row[0], atoi(row[1]), atoi(row[2]), atoi(row[3])); + fperm->st_uid = atoi(row[1]); + fperm->st_gid = atoi(row[2]); + fperm->st_mode = atoi(row[3]); + cnt = cnt + 1; + } + + if (cnt==0) { + sprintf(query, "insert into urls (url, uid, gid, perms) values ('%s', 0, 0, 0)", r->uri); + if (mysql_query(conn, query)) { + ap_rprintf(r, "%s\n
", mysql_error(conn)); + return(0); + } + ap_rprintf(r, "Aucune donnee\n
"); + } + + /* close connection */ + mysql_free_result(res); + mysql_close(conn); + return(0); +} + +//////////////////////////////////////////////////////////////// +/* Main routine - called after request processing */ +static int absec_handler_last(request_rec *r) +{ + // Is this module really called? + if (strcmp(r->handler, "absec")) { + return DECLINED; + } + + //////// + /* http method validate the perm asked (r/w vs get/post,put) */ + ap_rprintf(r, "After Method: %s
\r\n", r->method); + int permmask = 0; + if (strcmp(r->method,"GET")!=0) + { + // Not a GET, it's too late to do anything + ap_rprintf(r, "Not a GET post treatement is too late!\n
"); + return (DECLINED); + } + + r->content_type = "text/html"; + //ap_rprintf(r, "The sample page from mod_absec.c %s \n
", r->args); + ap_rprintf(r, "After GET Url: %s from %s \n
", r->filename, r->uri); + return (DECLINED); +} + +//////////////////////////////////////////////////////////////// +/* Main routine - called before request processing */ +static int absec_handler_first(request_rec *r) +{ + ap_rprintf(r, "Before Method: %s
\r\n", r->method); + // Is this module really called? + /*if (strcmp(r->handler, "absec")) { + ap_rprintf(r, "DECLINED
\r\n"); + return DECLINED; + }*/ + + + //////// + /* http method validate the perm asked (r/w vs get/post,put) */ + ap_rprintf(r, "Before Method: %s
\r\n", r->method); + + int permmask = 0; + if (strcmp(r->method,"GET")==0) permmask=0444; // r + if (strcmp(r->method,"PUT")==0) permmask=0222; // w + if (strcmp(r->method,"POST")==0) permmask=0222; // w + if (strcmp(r->method,"DELETE")==0) permmask=0111; // x + + //////// + /* check file permission on filesystem */ + /* should include */ + struct stat fperm; + int status; + //status = stat(r->filename, &fperm); + status = mysql_lookup(r, &fperm); + //ap_rprintf(r, "Result mysql: %d
\r\n", ); + if (status==-1) { + ap_rprintf(r, "stat erreur %d", errno); + return (OK); + } + //ap_rprintf(r, "File perms %o, owner %d, group %d (status %d)
\r\n", fperm.st_mode, fperm.st_uid, fperm.st_gid, status); + + /* check if any permission (ogw) match method (get r, put/post w, delete x) */ + if ((fperm.st_mode & permmask)==0) { + /* no permission match, return don't even have to check user perms */ + ap_rprintf(r, "Aucune permission pour la methode %s (%o, %o)", r->method, fperm.st_mode, permmask); + return (OK); + } + + // If file is world accessible for asked method return content + if (fperm.st_mode & 0x7 & permmask) { + /* if so, return, no need to check user perms */ + //ap_rprintf(r, "Fichier public
\r\n"); + return (DECLINED); + } + + //////// + /* Check if we have a basic auth user */ + const char* auth64p; + // Check if we have an auth header + auth64p = apr_table_get(r->headers_in,"Authorization"); + + // If no basic auth, ask for one + if (auth64p==NULL) { + r->content_type = "text/html"; + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"PAS DE USER ", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + } + + //////// + /* Retrieve user/pass from http basic auth header */ + // Get the basic auth base64 string and decode it + // Start at char 6 to skip 'Basic ' + char *auth64; + auth64 = apr_pstrdup(r->pool, auth64p+6); + char *auth; + auth = apr_pcalloc(r->pool, 64); + apr_base64_decode(auth, auth64); + char *user; + char *pass; + user = apr_strtok(auth, ":", &pass); + + r->content_type = "text/html"; + ap_rprintf(r, "Url: %s from %s \n
", r->filename, r->uri); + //ap_rprintf(r, "Headers Authorization: %s \n
", auth64); + //ap_rprintf(r, "User/Pass: %s/%s \n
", user, pass); + + //////// + // Connect to PAM to auth user + pam_handle_t * pamh = NULL; + int rret; + + if((rret = pam_start("httpd", user/*pw->pw_name*/, &conv, &pamh)) != PAM_SUCCESS) { + return HTTP_INTERNAL_SERVER_ERROR; + printf("Pam start failed\n"); + exit(0); + } + + // Set the PAM callback function response (would call for password) + reply = (struct pam_response *)malloc(sizeof(struct pam_response)); + reply[0].resp = strdup(pass); // password received in basic auth + reply[0].resp_retcode = 0; + + if((rret = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { + r->content_type = "text/html"; + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"USER/PASS INVALIDE ", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + printf("User auth failed\n"); + exit(0); + } + + if(pam_end(pamh, rret) != PAM_SUCCESS) { + //perror("pam_end"); + pamh = NULL; + return HTTP_INTERNAL_SERVER_ERROR; + exit(1); + } + + //////// + // Continue checking permission + + //////// + /* Retrieve user details from /etc/passwd to get uid and primary group */ + /* Should include */ + struct passwd *pw; + if((pw = getpwnam(user)) == NULL) + { + // Should never happend as already verified with PAM + ap_rprintf(r, "NULL \n
"); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"USER INCONNU ", ap_auth_name(r), + "\"", NULL)); + // User cannot be found, unauthorized + return HTTP_UNAUTHORIZED; + } + + // If file is user readable and user match return content + if ((fperm.st_uid==pw->pw_uid) && (fperm.st_mode & 0700 & permmask)) { + ap_rprintf(r, "Fichier propriétaire
\r\n"); + return (DECLINED); + } + + // If file is group readable and primary group match return content + if ((fperm.st_gid==pw->pw_gid) && (fperm.st_mode & 0070 & permmask)) { + ap_rprintf(r, "Fichier groupe
\r\n"); + return (DECLINED); + } + + //////// + /* Check supplemental groups */ + /* Should include */ + //ap_rprintf(r, "Fichier propriétaire %d %d %o %o
\r\n", fperm.st_uid, i, fperm.st_mode, 0400); + gid_t grouplist[16]; + int grouplistsize = 16; + int groupreturn; + groupreturn = getgrouplist(user, pw->pw_gid, grouplist, &grouplistsize); + if (groupreturn >= 0) { + ap_rprintf(r, "OK liste des groupes (%d)
\r\n", grouplistsize); + for (int i=0; i\r\n"); + return (DECLINED); + } + } + } + + // else decline request + ap_rprintf(r, "Aucuns droits de voir le fichier
\r\n"); + apr_table_setn(r->err_headers_out, + (PROXYREQ_PROXY == r->proxyreq) ? "Proxy-Authenticate" + : "WWW-Authenticate", + apr_pstrcat(r->pool, "Basic realm=\"NON AUTHORISE", ap_auth_name(r), + "\"", NULL)); + return HTTP_UNAUTHORIZED; + +} + +//////////////////////////////////////////////////////////////// +static void absec_register_hooks(apr_pool_t *p) +{ + //ap_hook_handler(absec_handler, NULL, NULL, APR_HOOK_MIDDLE); + ap_hook_handler(absec_handler_last, NULL, NULL, APR_HOOK_LAST); + ap_hook_handler(absec_handler_first, NULL, NULL, APR_HOOK_FIRST); + + // should use HOOK FIXUP + + // should use FILTER +} + +//////////////////////////////////////////////////////////////// +/* Dispatch list for API hooks */ +module AP_MODULE_DECLARE_DATA absec_module = { + STANDARD20_MODULE_STUFF, + NULL, /* create per-dir config structures */ + NULL, /* merge per-dir config structures */ + NULL, /* create per-server config structures */ + NULL, /* merge per-server config structures */ + NULL, /* table of config file commands */ + absec_register_hooks /* register hooks */ +}; + diff --git a/test_authen.c b/test_authen.c new file mode 100644 index 0000000..28283b6 --- /dev/null +++ b/test_authen.c @@ -0,0 +1,17 @@ +//#include "absec_pam.h" +//#include "absec_etc.h" +#include "absec_authen.h" +#include + +void main(int argc, char** argv){ + char *user = "jlcyr"; + char *pass = "jlcyrpass01!"; + printf("ABSEC auth pam for %s identified by %s\r\n", user, pass); + int retval = check_user(user, pass); + printf("Retval: %d\r\n", retval); + if (retval == PAM_ERROR_START) printf("Error at start\r\n"); + if (retval == PAM_ERROR_INVALID_CRED) printf("Invalid user/pass\r\n"); + if (retval == PAM_OK) printf("OK\r\n"); + printf("Test completed\r\n"); + return; +} diff --git a/test_etc.c b/test_etc.c new file mode 100644 index 0000000..5f7e681 --- /dev/null +++ b/test_etc.c @@ -0,0 +1,16 @@ +#include "absec_pam.h" +#include + +void main(int argc, char** argv){ + char *user = "jlcyr"; + char *pass = "jlcyrpass01!"; + printf("ABSEC auth etc for %s identified by %s\r\n", user, pass); + int retval; + retval = check_user(user, pass); + printf("Retval: %d\r\n", retval); + if (retval == PAM_ERROR_START) printf("Error at start\r\n"); + if (retval == PAM_ERROR_INVALID_CRED) printf("Invalid user/pass\r\n"); + if (retval == PAM_OK) printf("OK\r\n"); + printf("Test completed\r\n"); + return; +} diff --git a/test_mysql.c b/test_mysql.c index fad2db3..93eba3c 100755 --- a/test_mysql.c +++ b/test_mysql.c @@ -1,3 +1,6 @@ + + + #include #include diff --git a/test_pam.c b/test_pam.c new file mode 100644 index 0000000..95198ea --- /dev/null +++ b/test_pam.c @@ -0,0 +1,16 @@ +#include "absec_pam.h" +#include "absec_etc.h" +#include + +void main(int argc, char** argv){ + char *user = "jlcyr"; + char *pass = "jlcyrpass01!"; + printf("ABSEC auth pam for %s identified by %s\r\n", user, pass); + int retval = check_user(user, pass); + printf("Retval: %d\r\n", retval); + if (retval == PAM_ERROR_START) printf("Error at start\r\n"); + if (retval == PAM_ERROR_INVALID_CRED) printf("Invalid user/pass\r\n"); + if (retval == PAM_OK) printf("OK\r\n"); + printf("Test completed\r\n"); + return; +}