FreeRadius + PyOTP = My Home Wi-Fi With WPA-EAP-TTLS User/Pass/OTP Auth

Overview:

  1. Get the sqlite3 command & test the radius login locally to create the DB & OTP
  2. Insert a row into the “server” table with the @gmail.com login/pass of the sending email address
  3. Insert a row into the “login” table with the Wi-Fi login username, $salt$<wpa-pbkdf2(salt,password)>, & email/sms address

That’s it, now you have one time pads being sent to your phone for your home Wi-Fi enterprise login and you simply append those 6 digit numbers to the end of your password. Make sure to turn off your Wi-Fi password saving as well.

Source code can be found here:   [GIT Repo]

vim /etc/freeradius/sites-{available,enabled}/{default,inner-tunnel}

...
authorize {
    ...
    update control {
        Auth-Type := `/usr/bin/python /opt/freeradius/freeauth.py 86400 %{User-Name} %{User-Password}`
    }
    ...
}
...

vim /opt/freeradius/freeauth.py – needs correct freeradius permissions

#!/usr/bin/python

import crypt
import hashlib
import hmac
import os
import random
import re
import smtplib
import sqlite3
import string
import sys
import time

def sqloexec(sqloobjc, sqlocomd):
	try:
		sqloobjc.execute(sqlocomd)
	except:
		pass

def cleanstr(inputstr):
	outpstri = ""
	charlist = (string.digits + string.uppercase + string.lowercase)
	for inputchr in inputstr:
		if (inputchr in charlist):
			outpstri += inputchr
	return outpstri

def pbkdfsha(s, p):
	i = 1; l = ((256 / 8) * 2); d = ""
	while (len(d) < l):
		c = 0; rounds = 4096; f = ""
		lasthmac = (s + chr((i >> 24) & 0xff) + chr((i >> 16) & 0xff) + chr((i >> 8) & 0xff) + chr((i >> 0) & 0xff))
		while (c < rounds):
			u = hmac.new(p, lasthmac, hashlib.sha1).digest()
			lasthmac = u
			t = ""
			for x in range(0, len(u)):
				if (x >= len(f)):
					f += chr(0)
				t += chr(ord(f[x]) ^ ord(u[x]))
			f = t
			c += 1
		for j in f:
			q = ""
			if (ord(j) < 0x10):
				q = "0"
			if (len(d) < l):
				h = hex(ord(j))
				d += (q + h[2:])
		i += 1
	return d

denystri = "Reject"
authstri = "Accept"

mailuser = ""
mailpass = ""

expstime = int(sys.argv[1])
username = cleanstr(sys.argv[2])
userpass = sys.argv[3]
usercode = ""
regxobjc = re.match("^(.*[^0-9])([0-9]+)$", userpass)
if (regxobjc):
	userpass = str(regxobjc.group(1))
	usercode = str(regxobjc.group(2))
usermail = [""]
mailcode = ""

sqloconn = sqlite3.connect("/opt/freeradius/freeauth.db")
sqlocurs = sqloconn.cursor()

sqloexec(sqlocurs, "CREATE TABLE server (user TEXT, pass TEXT);")
sqloexec(sqlocurs, "CREATE TABLE login (user TEXT, pass TEXT, mail TEXT);")
sqloexec(sqlocurs, "CREATE TABLE token (loginuser TEXT, code TEXT, time INTEGER, exps INTEGER);")

for sqlorowo in sqlocurs.execute("SELECT * FROM server;"):
	mailuser = str(sqlorowo[0])
	mailpass = str(sqlorowo[1])

authflag = 0
for sqlorowo in sqlocurs.execute("SELECT * FROM login WHERE user = '" + username + "';"):
	sqlohash = str(sqlorowo[1])
	sqlomail = str(sqlorowo[2])
	sqloinfo = sqlohash.split("$")
	if (sqloinfo[2] == pbkdfsha(sqloinfo[1], userpass)):
		authflag = 1
		usermail[0] = sqlomail

if (authflag != 1):
	sqloconn.commit()
	sqloconn.close()
	sys.stdout.write(denystri);sys.exit(1)

prestime = int(time.time())
sqlocurs.execute("DELETE FROM token WHERE loginuser = '" + username + "' AND (" + str(prestime) + " - time) >= exps;")

authflag = 0
timelist = []
for sqlorowo in sqlocurs.execute("SELECT * FROM token WHERE loginuser = '" + username + "';"):
	sqlocode = str(sqlorowo[1])
	sqlotime = int(sqlorowo[2])
	if (sqlocode == usercode):
		authflag = 1
	timelist.append(sqlotime)
timelist.sort()

if (authflag == 1):
	sqloconn.commit()
	sqloconn.close()
	sys.stdout.write(authstri);sys.exit(0)

sendcode = 0
if (len(timelist) >= 1):
	if ((prestime - timelist[-1]) >= 60):
		sendcode = 1
else:
	sendcode = 1

if (sendcode == 1):
	for x in range(0, 6):
		mailcode += str(random.randint(0, 9))
	sqlocurs.execute("INSERT INTO token VALUES ('" + username + "', '" + mailcode + "', " + str(prestime) + ", " + str(expstime) + ");")

if ((mailuser != "") and (mailpass != "") and (usermail[0] != "") and (mailcode != "")):
	mailmesg = "\r\n".join([
		"From: " + mailuser,
		"To: " + usermail[0],
		"Subject: Code = " + mailcode,
		"",
		"OTP Auth Delivery"
	])
	
	server = smtplib.SMTP("smtp.gmail.com:587")
	server.ehlo()
	server.starttls()
	server.login(mailuser, mailpass)
	server.sendmail(mailuser, usermail, mailmesg)
	server.quit()

sqloconn.commit()
sqloconn.close()
sys.stdout.write(denystri);sys.exit(1)

…and!… a SSH PAM OTP too:

/*

gcc -fPIC -DPIC -shared -rdynamic -o sshdauth.so sshdauth.c
cp -fv sshdauth.so /lib/`uname -m`-linux-gnu/security/

vim /etc/pam.d/sshd
auth required sshdauth.so

vim /etc/ssh/sshd_config
UsePAM yes
UsePrivilegeSeparation no
PasswordAuthentication yes
ChallengeResponseAuthentication yes

*/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
#include <unistd.h>

#include <security/pam_appl.h>
#include <security/pam_modules.h>

int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char *argv[]) { return PAM_SUCCESS; }

int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv)
{
	int pame = 0;
	const char *umsg = "Username: ", *pmsg = "Password: ", *cmsg = "Passcode: ";
	const char *user = NULL, *pass = NULL, *code = NULL;
	struct pam_conv *conv;
	struct pam_message mesg;
	struct pam_response *resp;
	const struct pam_message *msgp;
	
	int link[2];
	char data[96], auth[2048];
	pid_t pidn;
	
	/* pam setup */
	
	pame = pam_get_user(pamh, &user, NULL);
	if ((pame != PAM_SUCCESS) || (user == NULL))
	{
		return PAM_AUTH_ERR;
	}
	
	pame = pam_get_authtok(pamh, PAM_AUTHTOK, (const char **)&pass, NULL);
	if (pame != PAM_SUCCESS)
	{
		return PAM_AUTH_ERR;
	}
	
	if (fork() == 0)
	{
		execl("/usr/bin/python", "python", "/opt/freeradius/freeauth.py", "120", user, pass, NULL);
		exit(0);
	}
	
	wait(NULL);
	
	pame = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
	if (pame != PAM_SUCCESS)
	{
		return PAM_AUTH_ERR;
	}
	//mesg.msg_style = PAM_PROMPT_ECHO_OFF;
	mesg.msg_style = PAM_PROMPT_ECHO_ON;
	mesg.msg = cmsg;
	msgp = &mesg;
	
	resp = NULL;
	pame = (*conv->conv)(1, &msgp, &resp, conv->appdata_ptr);
	if ((pame != PAM_SUCCESS) || (resp == NULL))
	{
		return PAM_AUTH_ERR;
	}
	code = resp->resp;
	
	/* auth check */
	
	if (pipe(link) == -1)
	{
		return PAM_AUTH_ERR;
	}
	
	pidn = fork();
	if (pidn == -1)
	{
		return PAM_AUTH_ERR;
	}
	
	if (pidn == 0)
	{
		dup2(link[1], STDOUT_FILENO);
		close(link[0]);
		bzero(auth, 2046 * sizeof(char));
		strncpy(auth, pass, 256);
		strncpy(auth + strlen(auth), code, 256);
		execl("/usr/bin/python", "python", "/opt/freeradius/freeauth.py", "120", user, auth, NULL);
		exit(0);
	}
	
	close(link[1]);
	bzero(data, 94 * sizeof(char));
	read(link[0], data, 92 * sizeof(char));
	wait(NULL);
	
	if (strcmp(data, "Accept") == 0)
	{
		return PAM_SUCCESS;
	}
	
	return PAM_AUTH_ERR;
}

FreeRadius + PyOTP = My Home Wi-Fi With WPA-EAP-TTLS User/Pass/OTP Auth

One thought on “FreeRadius + PyOTP = My Home Wi-Fi With WPA-EAP-TTLS User/Pass/OTP Auth

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s