184 lines
5.1 KiB
C
184 lines
5.1 KiB
C
#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 */
|
||
|
||
|
||
#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
|
||
|
||
#define ERR(...) \
|
||
fprintf(stderr, ANSI_COLOR_GREEN);\
|
||
fprintf(stderr, ##__VA_ARGS__);\
|
||
fprintf(stderr,ANSI_COLOR_RESET "\n");
|
||
|
||
|
||
int strArrMem(char * el, const char * arr[]){
|
||
int i = 0;
|
||
while(arr[i]!=NULL){
|
||
if(strcmp(el,arr[i]) == 0)
|
||
return 1;
|
||
i++;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int main(int argc, char **argv, char **envp){
|
||
|
||
const char const * authorizedCommands[] = {AUTHORIZED_COMMANDS};
|
||
|
||
NARGV* gitargv;
|
||
|
||
char* repoName;
|
||
|
||
char connInfo[BDD_CONN_LENGTH] = "dbname='pipi' user=pipisys password='";
|
||
FILE *dbPassFile;
|
||
int pos = strlen(connInfo);
|
||
|
||
char * userID;
|
||
|
||
PGconn *conn;
|
||
PGresult *res;
|
||
int nFields;
|
||
|
||
const char *accessTypeRequestValues[3];
|
||
char* niveauAutorisation;
|
||
|
||
char execFilename[MAX_FULL_EXECFILENAME_LENGTH] = COMMANDS_PATH;
|
||
|
||
pid_t gitPid;
|
||
|
||
|
||
// 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],AUTHORIZED_SHELL_FLAG)!=0){
|
||
ERR("Pas de bash ici, il vous faut un «vrai» accès SSH");
|
||
return 1;
|
||
}
|
||
|
||
// Parsing de la commande git
|
||
gitargv = nargv_parse(argv[2]);
|
||
if (gitargv->error_code) {
|
||
ERR("Impossible de comprendre la commande, erreur nargv %i: %s: at input column %i", gitargv->error_code, gitargv->error_message, gitargv->error_index);
|
||
return 1;
|
||
}
|
||
|
||
|
||
// Verification que la commande envoyée est bien une commande git
|
||
if(strArrMem(gitargv->argv[0],authorizedCommands)==0){
|
||
ERR("Wow ! ce canal est réservé aux commandes de données git, pour le reste, utilisez un «vrai» accès SSH");
|
||
nargv_free(gitargv);
|
||
return 1;
|
||
}
|
||
|
||
|
||
// Récupération de l'utilisateur
|
||
userID = getenv("GIT_USERID");
|
||
|
||
if(userID==NULL){
|
||
ERR("Impossible de récupérer votre identifiant utilisateur ...");
|
||
nargv_free(gitargv);
|
||
return 1;
|
||
}
|
||
|
||
|
||
// Récupération du repo et du droit d'accès demandés
|
||
// Normalement, la commande n'a que deux éléments:
|
||
|
||
repoName = gitargv->argv[1];
|
||
|
||
|
||
dbPassFile = fopen(BDD_PASS_FILE,"r");
|
||
if (dbPassFile == NULL) {
|
||
ERR("Cannot open file %s, on peut pas se connecter à la base de données en tant que l'uid %d", BDD_PASS_FILE, geteuid());
|
||
nargv_free(gitargv);
|
||
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) {
|
||
ERR("Connection to database failed: %s",PQerrorMessage(conn));
|
||
nargv_free(gitargv);
|
||
return 1;
|
||
}
|
||
res = PQexec(conn,
|
||
"SELECT pg_catalog.set_config('search_path', '', false)");
|
||
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||
ERR("SET failed: %s", PQerrorMessage(conn));
|
||
PQclear(res);
|
||
nargv_free(gitargv);
|
||
return 1;
|
||
}
|
||
PQclear(res);
|
||
|
||
// Récupération de l'autorisation auprès du serveur Pgsql
|
||
accessTypeRequestValues[0] = userID;
|
||
accessTypeRequestValues[1] = repoName;
|
||
accessTypeRequestValues[2] = (strcmp(gitargv->argv[0],"git-receive-pack")==0)?"WRITE":"READ";
|
||
res = PQexecParams(conn, "SELECT * FROM git.\"AccessibleRepos\"($1,$3) WHERE path=$2;",3,NULL,accessTypeRequestValues,NULL,NULL,1);
|
||
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||
ERR("Impossible de lancer la requête SQL pour les autorisations: %s",PQerrorMessage(conn));
|
||
PQclear(res);
|
||
nargv_free(gitargv);
|
||
PQfinish(conn);
|
||
return 1;
|
||
}
|
||
|
||
nFields = PQntuples(res);
|
||
PQclear(res);
|
||
PQfinish(conn);
|
||
|
||
if(nFields<1){
|
||
ERR("Vous n'avez pas le droit d'accéder à ce repo. Il n'existe peut-être même pas ...");
|
||
nargv_free(gitargv);
|
||
return 1;
|
||
}
|
||
|
||
// On effectue la commande.
|
||
// Pour l'instant execFilename = COMMANDS_PATH;
|
||
strcat(execFilename, gitargv->argv[0]);
|
||
|
||
|
||
gitPid=fork();
|
||
if (gitPid==0) { /* child process */
|
||
execve(execFilename, gitargv->argv, (char *const []){NULL}); // Pas d'environement
|
||
exit(127); /* only if execv fails */
|
||
}
|
||
else { /* pid!=0; parent process */
|
||
waitpid(gitPid,0,0); /* wait for child to exit */
|
||
}
|
||
|
||
|
||
|
||
nargv_free(gitargv);
|
||
return 0;
|
||
}
|