Premier commit.
Trois programmes: - authKeysPg génère un fichier .authorized_keys à partir de la BDD - pam_oath_key est un module PAM demandant un mot de passe otp selon la clé de connection SSH utilisée - bash-gitonly est un «shell» qui execute les commandes transmises par SSH, uniquement reliées à git et en vérifiant les permissions
This commit is contained in:
commit
04d63fd09f
4
.gitignore
vendored
Normal file
4
.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
auth-keys-gen
|
||||||
|
bash-gitonly
|
||||||
|
*.so
|
||||||
|
*.o
|
||||||
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
[submodule "nargv"]
|
||||||
|
path = nargv
|
||||||
|
url = https://github.com/hypersoft/nargv
|
||||||
20
Makefile
Normal file
20
Makefile
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
.PHONY: all install
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
auth-keys-gen: authKeysPg.c
|
||||||
|
gcc authKeysPg.c -o auth-keys-gen -L/usr/server/postgresql/lib -I/usr/server/postgresql/include -lpq
|
||||||
|
|
||||||
|
|
||||||
|
install: all
|
||||||
|
cp pam_oath_key.so /lib/security
|
||||||
|
cp bash-gitonly /srv/git/bin
|
||||||
|
cp auth-keys-gen /srv/etc/
|
||||||
98
authKeysPg.c
Normal file
98
authKeysPg.c
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include "libpq-fe.h"
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
static void
|
||||||
|
exit_nicely(PGconn *conn)
|
||||||
|
{
|
||||||
|
PQfinish(conn);
|
||||||
|
exit(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
|
||||||
|
PGconn *conn;
|
||||||
|
PGresult *res;
|
||||||
|
int nFields;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
|
||||||
|
/* 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);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Initialise un search path sûr, pour qu'un utilisateur
|
||||||
|
malveillant ne puisse prendre le contrôle. */
|
||||||
|
res = PQexec(conn,
|
||||||
|
"SELECT pg_catalog.set_config('search_path', '', false)");
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "SET failed: %s", PQerrorMessage(conn));
|
||||||
|
PQclear(res);
|
||||||
|
exit_nicely(conn);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Il faut libérer PGresult avec PQclear dès que l'on en a plus besoin pour
|
||||||
|
* éviter les fuites de mémoire.
|
||||||
|
*/
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
//res = PQprepare(conn,"GettingAllLoginGitUsersInfo","SELECT * FROM $1",1,NULL);
|
||||||
|
//const char * tablename = "git-users";
|
||||||
|
//res = PQexecPrepared(conn,"GettingAllLoginGitUsersInfo",1,"git-users",NULL,NULL,0);
|
||||||
|
|
||||||
|
res = PQexec(conn,"SELECT * FROM login.\"git-users\"");
|
||||||
|
/* affiche d'abord les noms des attributs */
|
||||||
|
nFields = PQnfields(res);
|
||||||
|
|
||||||
|
fprintf(stderr, PQerrorMessage(conn));
|
||||||
|
|
||||||
|
/* 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));
|
||||||
|
}
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
/* ferme la connexion à la base et nettoie */
|
||||||
|
PQfinish(conn);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
106
bash-gitonly.c
Normal file
106
bash-gitonly.c
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
#include <stdio.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#include "nargv/nargv.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_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
|
||||||
|
|
||||||
|
char* isCmdAuthorized(char* cmd){
|
||||||
|
const char* authorizedCommands[] = {AUTHORIZED_COMMANDS};
|
||||||
|
for (int i=0; i<AUTHORIZED_COMMANDS_COUNT-1;i++){
|
||||||
|
char * curseur = cmd;
|
||||||
|
const char * reader = authorizedCommands[i];
|
||||||
|
while(*curseur!=' ' && *curseur != '\0' && *curseur==*reader){
|
||||||
|
curseur++;
|
||||||
|
reader++;
|
||||||
|
}
|
||||||
|
if(*curseur == ' ' || *curseur == '\0')
|
||||||
|
return curseur;
|
||||||
|
}
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv, char **envp)
|
||||||
|
{
|
||||||
|
fprintf(stderr,"Printing environnement: \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){
|
||||||
|
fprintf(stderr, ANSI_COLOR_GREEN "Pas de bash ici, il vous faut un «vrai» accès SSH" ANSI_COLOR_RESET "\n");
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Verification que la commande envoyée est bien une commande git
|
||||||
|
char* sshCommandEnd = isCmdAuthorized(argv[2]);
|
||||||
|
if(sshCommandEnd == NULL){
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Récupération de l'utilisateur
|
||||||
|
// Récupération du repo et du droit d'accès demandés
|
||||||
|
// Récupération de l'autorisation auprès du serveur Pgsql
|
||||||
|
// On effectue la commande.
|
||||||
|
|
||||||
|
|
||||||
|
char execFilename[MAX_FULL_EXECFILENAME_LENGTH] = COMMANDS_PATH;
|
||||||
|
*sshCommandEnd = '\0';
|
||||||
|
strcat(execFilename, argv[2]);
|
||||||
|
*sshCommandEnd = ' ';
|
||||||
|
|
||||||
|
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});
|
||||||
|
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;
|
||||||
|
}
|
||||||
1
nargv
Submodule
1
nargv
Submodule
@ -0,0 +1 @@
|
|||||||
|
Subproject commit c022a75fad52e297bdb6ab98f026e58f516def00
|
||||||
376
pam_oath_key.c
Normal file
376
pam_oath_key.c
Normal file
@ -0,0 +1,376 @@
|
|||||||
|
#include "liboath/oath.h"
|
||||||
|
#include "libpq-fe.h"
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <stdarg.h>
|
||||||
|
#include <ctype.h>
|
||||||
|
|
||||||
|
|
||||||
|
// These #defines must be present according to PAM documentation.
|
||||||
|
#define PAM_SM_AUTH
|
||||||
|
//#define PAM_SM_ACCOUNT
|
||||||
|
//#define PAM_SM_SESSION
|
||||||
|
//#define PAM_SM_PASSWORD
|
||||||
|
|
||||||
|
|
||||||
|
#include <security/pam_appl.h>
|
||||||
|
#include <security/pam_modules.h>
|
||||||
|
|
||||||
|
|
||||||
|
// Les fonctions de debug
|
||||||
|
#define D(x) do {\
|
||||||
|
printf("[%s:%s(%d)] ", __FILE__, __FUNCTION__, __LINE__);\
|
||||||
|
printf x;\
|
||||||
|
printf("\n");\
|
||||||
|
} while (0)
|
||||||
|
#define DBG(x) if (cfg.debug) {\
|
||||||
|
D(x);\
|
||||||
|
}
|
||||||
|
|
||||||
|
#define PAM_EXTERN extern
|
||||||
|
|
||||||
|
// Static options
|
||||||
|
#define BDD_PASS_FILE "/srv/bdd/pipi-system.pass"
|
||||||
|
#define BDD_CONN_LENGTH 255
|
||||||
|
#define MIN_OTP_LEN 6
|
||||||
|
#define DEFAULT_OTP_LEN 6
|
||||||
|
#define MAX_OTP_LEN 8
|
||||||
|
#define OTP_SECRET_LEN 32
|
||||||
|
#define SSH_AUTH_INFO_LEN 1024
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
struct cfg {
|
||||||
|
int debug;
|
||||||
|
int digits;
|
||||||
|
int window;
|
||||||
|
};
|
||||||
|
|
||||||
|
static void
|
||||||
|
parse_cfg(int flags, int argc,
|
||||||
|
const char ** argv, struct cfg * cfg) {
|
||||||
|
int i;
|
||||||
|
|
||||||
|
cfg -> debug = 0;
|
||||||
|
cfg -> digits = 6;
|
||||||
|
cfg -> window = 5;
|
||||||
|
|
||||||
|
for (i = 0; i < argc; i++) {
|
||||||
|
if (strcmp(argv[i], "debug") == 0)
|
||||||
|
cfg -> debug = 1;
|
||||||
|
if (strncmp(argv[i], "digits=", 7) == 0)
|
||||||
|
cfg -> digits = atoi(argv[i] + 7);
|
||||||
|
if (strncmp(argv[i], "window=", 7) == 0)
|
||||||
|
cfg -> window = atoi(argv[i] + 7);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (MIN_OTP_LEN > cfg -> digits || cfg -> digits > MAX_OTP_LEN) {
|
||||||
|
D(("La longueur du code otp doit être entre %i et %i. Valeur invalide: digits=%i",MIN_OTP_LEN,MAX_OTP_LEN, cfg -> digits));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (cfg -> debug) {
|
||||||
|
D(("called."));
|
||||||
|
D(("flags %d argc %d", flags, argc));
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
D(("argv[%d]=%s", i, argv[i]));
|
||||||
|
D(("debug=%d", cfg -> debug));
|
||||||
|
D(("digits=%d", cfg -> digits));
|
||||||
|
D(("window=%d", cfg -> window));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
char * strtokk(char * str,const char cutter) {
|
||||||
|
|
||||||
|
while((*str != cutter) && (*str != '\0'))
|
||||||
|
str++;
|
||||||
|
|
||||||
|
bool strEnd = (*str == '\0');
|
||||||
|
*str = '\0';
|
||||||
|
if(!strEnd)
|
||||||
|
str++;
|
||||||
|
return str;
|
||||||
|
}
|
||||||
|
|
||||||
|
char hexCharReader(char hex){
|
||||||
|
if('0'<=hex && hex<='9')
|
||||||
|
return hex-'0';
|
||||||
|
else
|
||||||
|
return hex-'a'+10;
|
||||||
|
}
|
||||||
|
|
||||||
|
void hexToBytes(char * str, char arr[]){
|
||||||
|
int pos = 0;
|
||||||
|
while(*str != '\0'){
|
||||||
|
char b1 = hexCharReader(*str);
|
||||||
|
str++;
|
||||||
|
char b0 = hexCharReader(*str);
|
||||||
|
str++;
|
||||||
|
arr[pos] = b1<<4 | b0;
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PAM_EXTERN int
|
||||||
|
pam_sm_authenticate(pam_handle_t * pamh, int flags, int argc, const char ** argv) {
|
||||||
|
|
||||||
|
int retval, rc;
|
||||||
|
const char * user = NULL;
|
||||||
|
const char * password = NULL;
|
||||||
|
char otp[MAX_OTP_LEN + 1];
|
||||||
|
int password_len = 0;
|
||||||
|
struct pam_conv * conv;
|
||||||
|
struct pam_message * pmsg[1], msg[1];
|
||||||
|
struct pam_response * resp;
|
||||||
|
int nargs = 1;
|
||||||
|
struct cfg cfg;
|
||||||
|
char * query_prompt = NULL;
|
||||||
|
char * onlypasswd = strdup(""); /* empty passwords never match */
|
||||||
|
PGconn *conn;
|
||||||
|
PGresult *res;
|
||||||
|
int nFields;
|
||||||
|
char * oathSecret;
|
||||||
|
|
||||||
|
|
||||||
|
/***** Parsing config *****/
|
||||||
|
parse_cfg(flags, argc, argv, & cfg);
|
||||||
|
|
||||||
|
/***** Getting User *****/
|
||||||
|
retval = pam_get_user(pamh, &user, NULL);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
DBG(("Could not get PAM user: %s", pam_strerror(pamh, retval)));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
DBG(("We got the user: %s", user));
|
||||||
|
|
||||||
|
/****** Getting ssh key ******/
|
||||||
|
|
||||||
|
/* Retrieve SSH authentication information. */
|
||||||
|
char * ssh_auth_info_ret = (char *)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);
|
||||||
|
|
||||||
|
if (!ssh_auth_info || !*ssh_auth_info) {
|
||||||
|
DBG(("No SSH auth info, du coup on ignore"));
|
||||||
|
return PAM_IGNORE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const char delim = ' ';
|
||||||
|
|
||||||
|
char * authType = ssh_auth_info;
|
||||||
|
char * sshKeyType = strtokk(ssh_auth_info, delim);
|
||||||
|
char * sshKeyVal = strtokk(sshKeyType, delim);
|
||||||
|
|
||||||
|
DBG(("Authentification Type: '%s'", authType));
|
||||||
|
DBG(("Authentification KeyType: '%s'", sshKeyType));
|
||||||
|
DBG(("Authentification KeyVal: '%s'", sshKeyVal));
|
||||||
|
|
||||||
|
|
||||||
|
rc = oath_init();
|
||||||
|
if (rc != OATH_OK) {
|
||||||
|
DBG(("oath_init() failed (%d)", rc));
|
||||||
|
retval = PAM_AUTHINFO_UNAVAIL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
/****** Get oath secret from database ******/
|
||||||
|
char connInfo[BDD_CONN_LENGTH] = "dbname='pipi-system' user=pipiadmin password='";
|
||||||
|
char lastAp = '\'';
|
||||||
|
FILE *dbPassFile;
|
||||||
|
char ch;
|
||||||
|
int pos = strlen(connInfo);
|
||||||
|
|
||||||
|
|
||||||
|
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));
|
||||||
|
retval = PAM_AUTHINFO_UNAVAIL;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
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) {
|
||||||
|
DBG(("Connection to database failed: %s", PQerrorMessage(conn)));
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
res = PQexec(conn,
|
||||||
|
"SELECT pg_catalog.set_config('search_path', '', false)");
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||||||
|
DBG(("SET failed: %s", PQerrorMessage(conn)));
|
||||||
|
PQclear(res);
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG(("Initialisation de la base de données efféctuée"));
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
|
||||||
|
const char *paramValues[2];
|
||||||
|
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);
|
||||||
|
if (PQresultStatus(res) != PGRES_TUPLES_OK) {
|
||||||
|
DBG(("SET failed: %s", PQerrorMessage(conn)));
|
||||||
|
PQclear(res);
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
nFields = PQntuples(res);
|
||||||
|
if(nFields!=1){
|
||||||
|
DBG(("Aucun code secret oath trouvé pour la clé '%s'", sshKeyVal));
|
||||||
|
PQclear(res);
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
oathSecret=PQgetvalue(res, 0, 0);
|
||||||
|
|
||||||
|
DBG(("On a récupéré le code secret : %s",oathSecret));
|
||||||
|
|
||||||
|
PQclear(res);
|
||||||
|
PQfinish(conn);
|
||||||
|
|
||||||
|
|
||||||
|
/*** Parsing hex to byte array ***/
|
||||||
|
|
||||||
|
char oathSecretBin[OTP_SECRET_LEN];
|
||||||
|
int oathSecretBinLen = OTP_SECRET_LEN+1;
|
||||||
|
oath_hex2bin(oathSecret,oathSecretBin,&oathSecretBinLen);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/****** Asking for password ******/
|
||||||
|
retval = pam_get_item(pamh, PAM_CONV, (const void ** ) & conv);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
DBG(("get conv returned error: %s", pam_strerror(pamh, retval)));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
pmsg[0] = & msg[0];
|
||||||
|
{
|
||||||
|
const char * query_template = "One-time password (OATH) for `%s': ";
|
||||||
|
size_t len = strlen(query_template) + strlen(user);
|
||||||
|
size_t wrote;
|
||||||
|
|
||||||
|
query_prompt = malloc(len);
|
||||||
|
if (!query_prompt) {
|
||||||
|
retval = PAM_BUF_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
wrote = snprintf(query_prompt, len, query_template, user);
|
||||||
|
if (wrote < 0 || wrote >= len) {
|
||||||
|
retval = PAM_BUF_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg[0].msg = query_prompt;
|
||||||
|
}
|
||||||
|
|
||||||
|
msg[0].msg_style = PAM_PROMPT_ECHO_OFF;
|
||||||
|
resp = NULL;
|
||||||
|
|
||||||
|
retval = conv -> conv(nargs, (const struct pam_message ** ) pmsg, &
|
||||||
|
resp, conv -> appdata_ptr);
|
||||||
|
|
||||||
|
free(query_prompt);
|
||||||
|
query_prompt = NULL;
|
||||||
|
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
DBG(("conv returned error: %s", pam_strerror(pamh, retval)));
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG(("conv returned: %s", resp -> resp));
|
||||||
|
|
||||||
|
password = resp -> resp;
|
||||||
|
|
||||||
|
if (password)
|
||||||
|
password_len = strlen(password);
|
||||||
|
else {
|
||||||
|
DBG(("Could not read password"));
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (password_len < MIN_OTP_LEN) {
|
||||||
|
DBG(("OTP too short: %s", password));
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
} else if (password_len < cfg.digits) {
|
||||||
|
DBG(("OTP shorter than digits=%d: %s", cfg.digits, password));
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
} else if (password_len > MAX_OTP_LEN) {
|
||||||
|
DBG(("OTP too long (and no digits=): %s", password));
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
} else {
|
||||||
|
strcpy(otp, password);
|
||||||
|
password = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
DBG(("Pouf ! OTP: %s", otp ? otp : "(null)"));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/****** Validation du mot de passe oath ******/
|
||||||
|
{
|
||||||
|
time_t last_otp;
|
||||||
|
time_t jetzt = 0;
|
||||||
|
time(&jetzt);
|
||||||
|
int timeStep = 30;
|
||||||
|
DBG(("Validating oath secret %s of length %i at time %ld",oathSecret,oathSecretBinLen, jetzt));
|
||||||
|
char cdex[32];
|
||||||
|
rc = oath_totp_validate(oathSecretBin, oathSecretBinLen, jetzt, timeStep, 0, cfg.window, otp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rc == OATH_INVALID_OTP) {
|
||||||
|
DBG(("One-time password not authorized to login as user '%s'", user));
|
||||||
|
retval = PAM_AUTH_ERR;
|
||||||
|
goto done;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = PAM_SUCCESS;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/****** Terminé ! ******/
|
||||||
|
done:
|
||||||
|
|
||||||
|
oath_done();
|
||||||
|
free(query_prompt);
|
||||||
|
free(onlypasswd);
|
||||||
|
|
||||||
|
DBG(("Terminé ! [%s]", pam_strerror(pamh, retval)));
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
PAM_EXTERN
|
||||||
|
int pam_sm_setcred(pam_handle_t *pamh,int flags,int argc
|
||||||
|
,const char **argv)
|
||||||
|
{
|
||||||
|
return PAM_SUCCESS;
|
||||||
|
}
|
||||||
Loading…
x
Reference in New Issue
Block a user