Added bash-gitonly.c content, now working.

Added file-stored password for every bdd calls
This commit is contained in:
Mysaa Java 2021-05-12 02:20:00 +02:00
parent 04d63fd09f
commit a3a72a9549
5 changed files with 179 additions and 76 deletions

1
.gitignore vendored
View File

@ -2,3 +2,4 @@ auth-keys-gen
bash-gitonly
*.so
*.o
nargv/argvt.o

View File

@ -5,7 +5,7 @@ all: bash-gitonly pam_oath_key.so auth-keys-gen
nargv/argvt.o: nargv/nargv.c
gcc -c nargv/nargv.c -o nargv/argvt.o
bash-gitonly: nargv/argvt.o bash-gitonly.c
gcc bash-gitonly.c nargv/argvt.o -I . -o bash-gitonly
gcc bash-gitonly.c nargv/argvt.o -I . -L/usr/server/postgresql/lib -I/usr/server/postgresql/include -lpq -o bash-gitonly
pam_oath_key.so: pam_oath_key.c
gcc pam_oath_key.c -I/usr/system/include/ -I/usr/server/postgresql/include/ -L/usr/server/postgresql/lib/ -L/usr/libraries/lib/ -shared -lpam -lpq -loath -fPIC -o pam_oath_key.so
@ -16,5 +16,6 @@ auth-keys-gen: authKeysPg.c
install: all
cp pam_oath_key.so /lib/security
cp pam_oath_key.so /usr/libraries/lib/security/
cp bash-gitonly /srv/git/bin
cp auth-keys-gen /srv/etc/
cp auth-keys-gen /srv/etc/auth-git-keys

View File

@ -3,6 +3,13 @@
#include "libpq-fe.h"
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#define BDD_PASS_FILE "/srv/bdd/pipi-system.pass"
#define BDD_CONN_LENGTH 255
static void
exit_nicely(PGconn *conn)
{
@ -20,38 +27,39 @@ main(int argc, char **argv)
int j,i;
/*
char *password;
const char *passFileName = "/srv/bdd/pipi-system.pass";
FILE *passFile;
passFile = fopen(passFileName, "r");
fgets(password,127,passFile);
fclose(passFile);
printf("%-15s",password);
const char *connfirst = "dbname='pipi-system' user=pipiadmin password=";
//int lgt = strlen(connfirst) + strlen(password);
char * conninfo = (char *) malloc(256);
strcpy(conninfo,connfirst);
strcat(conninfo,password);
errno=0;
/* Crée une connexion à la base de données */
char *conninfo = "dbname='pipi-system' user=pipiadmin password='oaelEAAZH3Pr+hej43NnISY+RqkDpl09EHqzWu4XGQWUkG/Tkb+an2Triybhog/lkb/NRYK6b277duP0d3MF'";
conn = PQconnectdb(conninfo);
char connInfo[BDD_CONN_LENGTH] = "dbname='pipi' user=pipisys password='";
char lastAp = '\'';
FILE *dbPassFile;
char ch;
int pos = strlen(connInfo);
dbPassFile = fopen(BDD_PASS_FILE,"r");
if (dbPassFile == NULL) {
fprintf(stderr,"Cannot open file %s, on peut pas se connecter à la base de données pour lister les clés en tant que %d -> fopen error %d\n", BDD_PASS_FILE,getegid(),errno);
return 1;
}
while (feof(dbPassFile))
{
connInfo[pos] = fgetc(dbPassFile);
pos++;
}
fclose(dbPassFile);
connInfo[pos] = '\'';
conn = PQconnectdb(connInfo);
/* Vérifier que la connexion au backend a été faite avec succès */
if (PQstatus(conn) != CONNECTION_OK)
{
fprintf(stderr, "Connection to database failed: %s", PQerrorMessage(conn));
exit_nicely(conn);
return 1;
}
/* Initialise un search path sûr, pour qu'un utilisateur
@ -75,7 +83,7 @@ main(int argc, char **argv)
//const char * tablename = "git-users";
//res = PQexecPrepared(conn,"GettingAllLoginGitUsersInfo",1,"git-users",NULL,NULL,0);
res = PQexec(conn,"SELECT * FROM login.\"git-users\"");
res = PQexec(conn,"SELECT \"userID\",\"sshKeyType\",\"sshPubKey\" FROM git.keys");
/* affiche d'abord les noms des attributs */
nFields = PQnfields(res);
@ -84,15 +92,13 @@ main(int argc, char **argv)
/* puis affiche les lignes */
for (i = 0; i < PQntuples(res); i++)
{
printf("ssh-ed25519 %s\n",PQgetvalue(res,i,3));
//printf("environment=\"GIT_USER_UID=%s\" ssh-ed25519 %s\n",PQgetvalue(res, i, 1),PQgetvalue(res,i,3));
printf("environement=\"GIT_USERID=%s\" %s %s\n", (int) PQgetvalue(res,i,0),PQgetvalue(res,i,1),PQgetvalue(res,i,2));
}
PQclear(res);
/* ferme la connexion à la base et nettoie */
PQfinish(conn);
return 0;
}

View File

@ -1,9 +1,12 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdint.h>
#include "nargv/nargv.h"
#include "libpq-fe.h"
#include <unistd.h> /* for fork */
#include <sys/types.h> /* for pid_t */
#include <sys/wait.h> /* for wait */
@ -12,11 +15,16 @@
#define ANSI_COLOR_GREEN "\x1b[32m"
#define ANSI_COLOR_RESET "\x1b[0m"
#define AUTHORIZED_SHELL_COMMAND "bash-gitonly"
#define AUTHORIZED_SHELL_FLAG "-c"
#define AUTHORIZED_COMMANDS "git-receive-pack","git-upload-pack","git-upload-archive"
#define AUTHORIZED_COMMANDS_COUNT 3
#define COMMANDS_PATH "/bin/"
#define MAX_FULL_EXECFILENAME_LENGTH 24
#define BDD_PASS_FILE "/pipi-system.pass"
#define BDD_CONN_LENGTH 255
char* isCmdAuthorized(char* cmd){
const char* authorizedCommands[] = {AUTHORIZED_COMMANDS};
@ -33,23 +41,21 @@ char* isCmdAuthorized(char* cmd){
return NULL;
}
int main(int argc, char **argv, char **envp)
{
fprintf(stderr,"Printing environnement: \n");
int main(int argc, char **argv, char **envp){
fprintf(stderr,"Received argv:\n");
for(int i=0;i<argc;i++)
{
fprintf(stderr,"%s\n",argv[i]);
}
fprintf(stderr,"Received env:\n");
for (char **env = envp; *env != 0; env++)
{
char *thisEnv = *env;
fprintf(stderr,"%s\n", thisEnv);
}
int counter;
for(counter=0; counter<argc; counter++)
printf("argv[%2d]: %s\n",counter,argv[counter]);
const char* sshcmdname = "/bin/bash-gitonly";
const char* sshcmdflags = "-c";
// Ce bout de code est ultra-restrictif et risque de planter si SSH change. Le message sera alors reconaissable.
if(argc !=3 || (strcmp(argv[0],AUTHORIZED_SHELL_COMMAND)!=0 && strcmp(argv[0],COMMANDS_PATH AUTHORIZED_SHELL_COMMAND)) || strcmp(argv[1],sshcmdflags)!=0){
if(argc !=3 || (strcmp(argv[0],AUTHORIZED_SHELL_COMMAND)!=0 && strcmp(argv[0],COMMANDS_PATH AUTHORIZED_SHELL_COMMAND)) || strcmp(argv[1],AUTHORIZED_SHELL_FLAG)!=0){
fprintf(stderr, ANSI_COLOR_GREEN "Pas de bash ici, il vous faut un «vrai» accès SSH" ANSI_COLOR_RESET "\n");
return 1;
}
@ -60,47 +66,124 @@ int main(int argc, char **argv, char **envp)
fprintf(stderr, ANSI_COLOR_GREEN "Wow ! ce canal est réservé aux commandes de données git, pour le reste, utilisez un «vrai» accès SSH" ANSI_COLOR_RESET "\n");
return 1;
}
// Parsing de la commande git
NARGV* subargv = nargv_parse(argv[2]);
if (subargv->error_code) {
fprintf(stderr,ANSI_COLOR_GREEN "Impossible de comprendre la commande, erreur nargv %i: %s: at input column %i" ANSI_COLOR_RESET "\n",
subargv->error_code, subargv->error_message, subargv->error_index);
return 1;
}
char* subcmdName = subargv->argv[0];
// Récupération de l'utilisateur
char * userID = getenv("GIT_USERID");
if(userID==NULL){
fprintf(stderr,ANSI_COLOR_GREEN "Impossible de récupérer votre identifiant utilisateur ..." ANSI_COLOR_RESET "\n");
return 1;
}
// Récupération du repo et du droit d'accès demandés
// Récupération de l'autorisation auprès du serveur Pgsql
// Normalement, la commande n'a que deux éléments:
char* repoName = subargv->argv[1];
char connInfo[BDD_CONN_LENGTH] = "dbname='pipi' user=pipisys password='";
char lastAp = '\'';
FILE *dbPassFile;
char ch;
int pos = strlen(connInfo);
dbPassFile = fopen(BDD_PASS_FILE,"r");
if (dbPassFile == NULL) {
fprintf(stderr,ANSI_COLOR_GREEN "Cannot open file %s, on peut pas se connecter à la base de données" ANSI_COLOR_RESET "\n",BDD_PASS_FILE);
return 1;
}
while (feof(dbPassFile))
{
connInfo[pos] = fgetc(dbPassFile);
pos++;
}
fclose(dbPassFile);
connInfo[pos] = '\'';
PGconn *conn;
conn = PQconnectdb(connInfo);
PGresult *res;
int nFields;
/* Vérifier que la connexion au backend a été faite avec succès */
if (PQstatus(conn) != CONNECTION_OK) {
fprintf(stderr,ANSI_COLOR_GREEN "Connection to database failed: %s" ANSI_COLOR_RESET "\n",PQerrorMessage(conn));
return 1;
}
res = PQexec(conn,
"SELECT pg_catalog.set_config('search_path', '', false)");
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr,ANSI_COLOR_GREEN "SET failed: %s" ANSI_COLOR_RESET "\n",PQerrorMessage(conn));
PQclear(res);
return 1;
}
PQclear(res);
// Récupération de l'autorisation auprès du serveur Pgsql
const char *paramValues[2];
paramValues[0] = userID;
paramValues[1] = repoName;
res = PQexecParams(conn, "SELECT git.\"AccessType\"($1,$2)",2,NULL,paramValues,NULL,NULL,1);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
fprintf(stderr,ANSI_COLOR_GREEN "Impossible de lancer la requête SQL pour les autorisations: %s" ANSI_COLOR_RESET "\n",PQerrorMessage(conn));
PQclear(res);
return 1;
}
nFields = PQntuples(res);
// Il y a toujours une seule valeur retournée, renvoie NULL si il n'y a rien
char* niveauAutorisation;
niveauAutorisation=PQgetvalue(res, 0, 0);
PQclear(res);
PQfinish(conn);
fprintf(stderr,ANSI_COLOR_GREEN "Autorisation: sur %s pour %s -> %s" ANSI_COLOR_RESET "\n",userID,repoName,niveauAutorisation);
if(niveauAutorisation[0]=='\0'){// If the string is empty i.e. NULL authorisations
fprintf(stderr,ANSI_COLOR_GREEN "Vous n'avez pas le droit d'accéder à ce repo. Il n'existe peut-être même pas ..." ANSI_COLOR_RESET "\n");
return 1;
}
// Déjà on a la lecture
if(strcmp(subcmdName,"git-receive-pack")==0){
// Il nous faut aussi le droit d'écriture
if(strcmp(niveauAutorisation,"WRITE")!=0){
fprintf(stderr,ANSI_COLOR_GREEN "Vous n'avez pas le droit d'écrire dans ce repo." ANSI_COLOR_RESET "\n");
return 1;
}
}
// On effectue la commande.
char execFilename[MAX_FULL_EXECFILENAME_LENGTH] = COMMANDS_PATH;
*sshCommandEnd = '\0';
strcat(execFilename, argv[2]);
*sshCommandEnd = ' ';
strcat(execFilename, subcmdName);
const char* sshArgs = sshCommandEnd+1;
NARGV* subargv = nargv_parse(argv[2]);
fprintf(stderr,"Trying to parse '%s': %i args\n",sshArgs,subargv->argc);
fprintf(stderr,"Error code: %i\n",subargv->error_code);
for(int i = 0;i<subargv->argc;i++){
fprintf(stderr,"%02d -> %s\n",i,subargv->argv[i]);
}
if (subargv->error_code) {
fprintf(stderr,"nargv parse error: %i: %s: at input column %i\n",
subargv->error_code, subargv->error_message, subargv->error_index);
return 2;
}
pid_t pid=fork();
if (pid==0) { /* child process */
//static char *argv[]={"echo","Foo is my name.",NULL};
execve(execFilename, subargv->argv, (char *const []){NULL});
execve(execFilename, subargv->argv, (char *const []){NULL}); // Pas d'environement
exit(127); /* only if execv fails */
}
else { /* pid!=0; parent process */
waitpid(pid,0,0); /* wait for child to exit */
}
nargv_free(subargv);
return 0;
}

View File

@ -5,6 +5,9 @@
#include <stdarg.h>
#include <ctype.h>
#include <unistd.h>
#include <sys/types.h>
// These #defines must be present according to PAM documentation.
#define PAM_SM_AUTH
@ -30,7 +33,7 @@
#define PAM_EXTERN extern
// Static options
#define BDD_PASS_FILE "/srv/bdd/pipi-system.pass"
#define BDD_PASS_FILE "/srv/bdd/pipi-system.pass2"
#define BDD_CONN_LENGTH 255
#define MIN_OTP_LEN 6
#define DEFAULT_OTP_LEN 6
@ -51,7 +54,7 @@ parse_cfg(int flags, int argc,
const char ** argv, struct cfg * cfg) {
int i;
cfg -> debug = 0;
cfg -> debug = 1;
cfg -> digits = 6;
cfg -> window = 5;
@ -113,6 +116,7 @@ void hexToBytes(char * str, char arr[]){
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char ** argv) {
int retval, rc;
const char * user = NULL;
@ -131,7 +135,12 @@ pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char ** argv
int nFields;
char * oathSecret;
FILE* stderr2 = fopen("/tmp/pam-oath.log","w");
D(("Running the oath authenticator !\n"));
fprintf(stderr2,"Ca a au moins passé l'init ...");
/***** Parsing config *****/
parse_cfg(flags, argc, argv, & cfg);
@ -142,19 +151,22 @@ pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char ** argv
goto done;
}
DBG(("We got the user: %s", user));
fprintf(stderr2,"Le user est là ! %s", user);
/****** Getting ssh key ******/
/* Retrieve SSH authentication information. */
char * ssh_auth_info_ret = (char *)pam_getenv(pamh, "SSH_AUTH_INFO_0");
const char * ssh_auth_info_ret = pam_getenv(pamh, "SSH_AUTH_INFO_0");
char ssh_auth_info [SSH_AUTH_INFO_LEN];
ssh_auth_info_ret = "pubkey ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKXQt1YWjKCsjcsFW7o1hdjAB/qxWBwesAeV0RcBeW0I";
strcpy(ssh_auth_info,ssh_auth_info_ret);
//ssh_auth_info_ret = "pubkey ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIKXQt1YWjKCsjcsFW7o1hdjAB/qxWBwesAeV0RcBeW0I";
if (!ssh_auth_info || !*ssh_auth_info) {
DBG(("No SSH auth info, du coup on ignore"));
DBG(("No SSH auth info, impossible de traiter"));
return PAM_IGNORE;
}
strcpy(ssh_auth_info,ssh_auth_info_ret);
DBG(("Infos de connection: %s",ssh_auth_info));
const char delim = ' ';
@ -175,16 +187,16 @@ pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char ** argv
}
/****** Get oath secret from database ******/
char connInfo[BDD_CONN_LENGTH] = "dbname='pipi-system' user=pipiadmin password='";
char connInfo[BDD_CONN_LENGTH] = "dbname='pipi' user=pipisys password='";
char lastAp = '\'';
FILE *dbPassFile;
char ch;
int pos = strlen(connInfo);
printf("WOW ! Mais c'est génial ici !");
dbPassFile = fopen(BDD_PASS_FILE,"r");
if (dbPassFile == NULL) {
DBG(("Cannot open file %s, on peut pas se connecter à la base de données\n", BDD_PASS_FILE));
DBG(("Cannot open file %s, on peut pas se connecter à la base de données avec l'UID %s\n", BDD_PASS_FILE, geteuid()));
retval = PAM_AUTHINFO_UNAVAIL;
goto done;
}
@ -224,7 +236,7 @@ pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char ** argv
paramValues[0] = sshKeyType;
paramValues[1] = sshKeyVal;
res = PQexecParams(conn, "SELECT \"oathPrivate\" FROM login.git WHERE \"sshKeyType\"=$1 AND \"sshPubKey\"=$2",2,NULL,paramValues,NULL,NULL,1);
res = PQexecParams(conn, "SELECT \"oathPrivate\" FROM git.keys WHERE \"sshKeyType\"=$1 AND \"sshPubKey\"=$2",2,NULL,paramValues,NULL,NULL,1);
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
DBG(("SET failed: %s", PQerrorMessage(conn)));
PQclear(res);