/* ** 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 ** ** you immediately can request the URL /absec and watch for the ** output of this module. This can be achieved for instance via: ** ** $ lynx -mime_header http://localhost/absec ** ** The output should be similar to the following one: ** ** HTTP/1.1 200 OK ** Date: Tue, 31 Mar 1998 14:42:22 GMT ** Server: Apache/1.3.4 (Unix) ** Connection: close ** Content-Type: text/html ** ** The sample page from mod_absec.c */ /* 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 #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; } //int function_conversation ( ) { /* ToDo prompt user for input */ //}; //struct pam_conv conv = { function_conversation, 0 }; struct pam_response *reply; int converse(int n, const struct pam_message **msg, struct pam_response **resp, void *data) { *resp = reply; return PAM_SUCCESS; struct pam_response *aresp; char buf[PAM_MAX_RESP_SIZE]; int i; data = data; if (n <= 0 || n > PAM_MAX_NUM_MSG) return (PAM_CONV_ERR); if ((aresp = calloc(n, sizeof *aresp)) == NULL) return (PAM_BUF_ERR); for (i = 0; i < n; ++i) { aresp[i].resp_retcode = 0; aresp[i].resp = NULL; switch (msg[i]->msg_style) { case PAM_PROMPT_ECHO_OFF: aresp[i].resp = strdup("jlcyrpass01!"); //aresp[i].resp = strdup(getpass(msg[i]->msg)); if (aresp[i].resp == NULL) goto fail; break; case PAM_PROMPT_ECHO_ON: fputs(msg[i]->msg, stderr); //if (fgets(buf, sizeof buf, stdin) == NULL) // goto fail; //aresp[i].resp = strdup(buf); aresp[i].resp = strdup("jlcyrpass01!"); if (aresp[i].resp == NULL) goto fail; break; case PAM_ERROR_MSG: fputs(msg[i]->msg, stderr); if (strlen(msg[i]->msg) > 0 && msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n') fputc('\n', stderr); break; case PAM_TEXT_INFO: fputs(msg[i]->msg, stdout); if (strlen(msg[i]->msg) > 0 && msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n') fputc('\n', stdout); break; default: goto fail; } } *resp = aresp; return (PAM_SUCCESS); fail: for (i = 0; i < n; ++i) { if (aresp[i].resp != NULL) { memset(aresp[i].resp, 0, strlen(aresp[i].resp)); free(aresp[i].resp); } } memset(aresp, 0, n * sizeof *aresp); *resp = NULL; return (PAM_CONV_ERR); } struct pam_conv conv = { converse, 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, "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); //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; }*/ pam_handle_t * pamh = NULL; int rret; if((rret = pam_start("httpd", pw->pw_name, &conv, &pamh)) != PAM_SUCCESS) { return HTTP_INTERNAL_SERVER_ERROR; printf("Pam start failed\n"); exit(0); } /* if((rret = pam_set_item( pamh, PAM_AUTHTOK, &pass)) == PAM_BUF_ERR) { return HTTP_BAD_REQUEST; } */ reply = (struct pam_response *)malloc(sizeof(struct pam_response)); // *** Get the password by any method, or maybe it was passed into this function. reply[0].resp = strdup(pass); reply[0].resp_retcode = 0; if((rret = pam_authenticate(pamh, 0)) != PAM_SUCCESS) { 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); } //////// /* 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); } return HTTP_OK; //////// /* 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("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 */ };