first stable
This commit is contained in:
parent
da7e160b9f
commit
bb55ad76e3
|
@ -0,0 +1,2 @@
|
||||||
|
bin
|
||||||
|
.vscode
|
|
@ -1,3 +1,10 @@
|
||||||
# CHecKPASSword
|
# CHecKPASSword
|
||||||
|
|
||||||
Проверка и смена пароля пользователя с использованием библиотеки PAM
|
Checking and changing the user's password using the PAM library and its modules
|
||||||
|
|
||||||
|
To use it, you need to modify the binary file:
|
||||||
|
|
||||||
|
```
|
||||||
|
chown root:root /<path-to>/chkpass
|
||||||
|
chmod 4755 /<path-to>/chkpass
|
||||||
|
```
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
check = login
|
||||||
|
change = passwd
|
|
@ -0,0 +1,22 @@
|
||||||
|
{
|
||||||
|
"name": "chkpass",
|
||||||
|
"authors": [
|
||||||
|
"Alexander Zhirov <alexander@zhirov.kz>"
|
||||||
|
],
|
||||||
|
"license": "GPL-2.0",
|
||||||
|
"copyright": "© Alexander Zhirov, 2024",
|
||||||
|
"description": "Checking and changing the user's password using the PAM library and its modules",
|
||||||
|
"targetPath": "bin",
|
||||||
|
"targetType": "executable",
|
||||||
|
"dependencies": {
|
||||||
|
"singlog": "~>0.5.0",
|
||||||
|
"readconf": "~>0.4.1",
|
||||||
|
"commandr": "~>1.1.0"
|
||||||
|
},
|
||||||
|
"libs": [
|
||||||
|
"pam"
|
||||||
|
],
|
||||||
|
"preBuildCommands": [
|
||||||
|
"./install-debian-dependencies.sh"
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,10 @@
|
||||||
|
{
|
||||||
|
"fileVersion": 1,
|
||||||
|
"versions": {
|
||||||
|
"commandr": "1.1.0",
|
||||||
|
"datefmt": "1.0.4",
|
||||||
|
"readconf": "0.4.1",
|
||||||
|
"silly": "1.2.0-dev.2",
|
||||||
|
"singlog": "0.5.0"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# apt install -y libpam-dev
|
||||||
|
# eopkg it -y pam-devel
|
|
@ -0,0 +1,76 @@
|
||||||
|
import chkpass;
|
||||||
|
|
||||||
|
import commandr;
|
||||||
|
import singlog;
|
||||||
|
import core.stdc.stdlib : EXIT_SUCCESS, EXIT_FAILURE;
|
||||||
|
import std.stdio : writeln;
|
||||||
|
|
||||||
|
private string programName = "chkpass";
|
||||||
|
|
||||||
|
int main(string[] args) {
|
||||||
|
auto argumets = new Program(programName, chkpassVersion)
|
||||||
|
.add(new Command("check", "check user password")
|
||||||
|
.add(new Option("m", "module", "use a dedicated PAM module")
|
||||||
|
.optional)
|
||||||
|
.add(new Argument("username")
|
||||||
|
.required)
|
||||||
|
.add(new Argument("password")
|
||||||
|
.required))
|
||||||
|
.add(new Command("change", "change user password")
|
||||||
|
.add(new Option("m", "module", "use a dedicated PAM module")
|
||||||
|
.optional)
|
||||||
|
.add(new Argument("username")
|
||||||
|
.required)
|
||||||
|
.add(new Argument("password")
|
||||||
|
.required)
|
||||||
|
.add(new Argument("new-password")
|
||||||
|
.required))
|
||||||
|
.parse(args);
|
||||||
|
|
||||||
|
string pamod, user, password, newPassword, command;
|
||||||
|
|
||||||
|
argumets
|
||||||
|
.on("check", (a) {
|
||||||
|
command = a.name;
|
||||||
|
pamod = a.option("module");
|
||||||
|
user = a.arg("username");
|
||||||
|
password = a.arg("password");
|
||||||
|
})
|
||||||
|
.on("change", (a) {
|
||||||
|
command = a.name;
|
||||||
|
pamod = a.option("module");
|
||||||
|
user = a.arg("username");
|
||||||
|
password = a.arg("password");
|
||||||
|
newPassword = a.arg("new-password");
|
||||||
|
});
|
||||||
|
|
||||||
|
log.output(log.output.syslog)
|
||||||
|
.program(programName)
|
||||||
|
.level(log.level.error);
|
||||||
|
|
||||||
|
string configFile = "/etc/chkpass/chkpass.conf";
|
||||||
|
auto settings = readConfigFile(configFile);
|
||||||
|
|
||||||
|
auto auth = new Auth;
|
||||||
|
|
||||||
|
final switch(command) {
|
||||||
|
case "check":
|
||||||
|
if (!pamod.length) pamod = settings.check;
|
||||||
|
if (auth.authenticate(pamod, user, password)) {
|
||||||
|
writeln("Password verification failed");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
writeln("Password verification successful");
|
||||||
|
break;
|
||||||
|
case "change":
|
||||||
|
if (!pamod.length) pamod = settings.change;
|
||||||
|
if (auth.changePassword(pamod, user, password, newPassword)) {
|
||||||
|
writeln("The password has not been changed");
|
||||||
|
return EXIT_FAILURE;
|
||||||
|
}
|
||||||
|
writeln("The password was successfully changed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return EXIT_SUCCESS;
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
module chkpass.libpam;
|
||||||
|
|
||||||
|
public import chkpass.libpam.pam;
|
||||||
|
public import chkpass.libpam.types;
|
|
@ -0,0 +1,11 @@
|
||||||
|
module chkpass.libpam.pam;
|
||||||
|
|
||||||
|
import chkpass.libpam.types;
|
||||||
|
|
||||||
|
extern(C):
|
||||||
|
|
||||||
|
int pam_start(const(char) *service_name, const(char) *user, const PamConv *pam_conversation, PamConv **pamh);
|
||||||
|
int pam_authenticate(PamConv *pamh, int flags);
|
||||||
|
int pam_end(PamConv *pamh, int pam_status);
|
||||||
|
int pam_chauthtok(PamConv *pamh, int flags);
|
||||||
|
int pam_acct_mgmt(PamConv *pamh, int flags);
|
|
@ -0,0 +1,61 @@
|
||||||
|
module chkpass.libpam.types;
|
||||||
|
|
||||||
|
extern(C):
|
||||||
|
|
||||||
|
struct PamMessage {
|
||||||
|
int msg_style;
|
||||||
|
const(char) *msg;
|
||||||
|
}
|
||||||
|
|
||||||
|
struct PamResponse {
|
||||||
|
char *resp;
|
||||||
|
int resp_retcode;
|
||||||
|
}
|
||||||
|
|
||||||
|
alias conversation = int function(int num_msg, const PamMessage **msg, PamResponse **resp, void *appdata_ptr);
|
||||||
|
|
||||||
|
struct PamConv {
|
||||||
|
conversation *conv;
|
||||||
|
void *appdata_ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
const (char) *pam_strerror(PamConv *pamh, int errnum);
|
||||||
|
|
||||||
|
enum PAM_SUCCESS = 0;
|
||||||
|
enum PAM_OPEN_ERR = 1;
|
||||||
|
enum PAM_SYMBOL_ERR = 2;
|
||||||
|
enum PAM_SERVICE_ERR = 3;
|
||||||
|
enum PAM_SYSTEM_ERR = 4;
|
||||||
|
enum PAM_BUF_ERR = 5;
|
||||||
|
enum PAM_PERM_DENIED = 6;
|
||||||
|
enum PAM_AUTH_ERR = 7;
|
||||||
|
enum PAM_CRED_INSUFFICIENT = 8;
|
||||||
|
enum PAM_AUTHINFO_UNAVAIL = 9;
|
||||||
|
enum PAM_USER_UNKNOWN = 10;
|
||||||
|
enum PAM_MAXTRIES = 11;
|
||||||
|
enum PAM_NEW_AUTHTOK_REQD = 12;
|
||||||
|
enum PAM_ACCT_EXPIRED = 13;
|
||||||
|
enum PAM_SESSION_ERR = 14;
|
||||||
|
enum PAM_CRED_UNAVAIL = 15;
|
||||||
|
enum PAM_CRED_EXPIRED = 16;
|
||||||
|
enum PAM_CRED_ERR = 17;
|
||||||
|
enum PAM_NO_MODULE_DATA = 18;
|
||||||
|
enum PAM_CONV_ERR = 19;
|
||||||
|
enum PAM_AUTHTOK_ERR = 20;
|
||||||
|
enum PAM_AUTHTOK_RECOVERY_ERR = 21;
|
||||||
|
enum PAM_AUTHTOK_LOCK_BUSY = 22;
|
||||||
|
enum PAM_AUTHTOK_DISABLE_AGING = 23;
|
||||||
|
enum PAM_TRY_AGAIN = 24;
|
||||||
|
enum PAM_IGNORE = 25;
|
||||||
|
enum PAM_ABORT = 26;
|
||||||
|
enum PAM_AUTHTOK_EXPIRED = 27;
|
||||||
|
enum PAM_MODULE_UNKNOWN = 28;
|
||||||
|
enum PAM_BAD_ITEM = 29;
|
||||||
|
enum PAM_CONV_AGAIN = 30;
|
||||||
|
enum PAM_INCOMPLETE = 31;
|
||||||
|
enum _PAM_RETURN_VALUES = 32;
|
||||||
|
|
||||||
|
enum PAM_PROMPT_ECHO_OFF = 1;
|
||||||
|
enum PAM_PROMPT_ECHO_ON = 2;
|
||||||
|
enum PAM_ERROR_MSG = 3;
|
||||||
|
enum PAM_TEXT_INFO = 4;
|
|
@ -0,0 +1,6 @@
|
||||||
|
module chkpass;
|
||||||
|
|
||||||
|
public import chkpass.libpam;
|
||||||
|
public import chkpass.user;
|
||||||
|
public import chkpass.utils;
|
||||||
|
public import chkpass.version_;
|
|
@ -0,0 +1,164 @@
|
||||||
|
module chkpass.user.auth;
|
||||||
|
|
||||||
|
import chkpass.libpam;
|
||||||
|
|
||||||
|
import std.string : toStringz;
|
||||||
|
import core.stdc.stdlib;
|
||||||
|
import std.conv;
|
||||||
|
import core.stdc.string : strdup, strstr;
|
||||||
|
import std.format;
|
||||||
|
|
||||||
|
import singlog;
|
||||||
|
|
||||||
|
enum {
|
||||||
|
AUTH_SUCCESS = 0,
|
||||||
|
AUTH_ERR_USER = 1,
|
||||||
|
AUTH_ERR_PASS = 2,
|
||||||
|
AUTH_ERR_NPASS = 3,
|
||||||
|
AUTH_ERR_START = 4,
|
||||||
|
AUTH_ERR_AUTH = 5,
|
||||||
|
AUTH_ERR_ACCT = 6,
|
||||||
|
AUTH_ERR_CHTOK = 7,
|
||||||
|
AUTH_ERR_END = 8
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
struct PAMdata {
|
||||||
|
string password;
|
||||||
|
string newPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
extern(C) int conversation_func(int num_msg, const PamMessage **msg, PamResponse **resp, void *appdata_ptr) {
|
||||||
|
PAMdata *data = cast(PAMdata*)appdata_ptr;
|
||||||
|
|
||||||
|
PamResponse *responses = cast(PamResponse *)calloc(num_msg, PamResponse.sizeof);
|
||||||
|
|
||||||
|
if (responses == null) {
|
||||||
|
return PAM_BUF_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int count = 0; count < num_msg; ++count) {
|
||||||
|
responses[count].resp_retcode = 0;
|
||||||
|
switch (msg[count].msg_style) {
|
||||||
|
case PAM_PROMPT_ECHO_ON:
|
||||||
|
case PAM_PROMPT_ECHO_OFF:
|
||||||
|
switch (msg[count].msg.to!string) {
|
||||||
|
case "New password: ":
|
||||||
|
case "Retype new password: ":
|
||||||
|
responses[count].resp = strdup(data.newPassword.toStringz);
|
||||||
|
break;
|
||||||
|
case "Password: ":
|
||||||
|
case "Current password: ":
|
||||||
|
responses[count].resp = strdup(data.password.toStringz);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
responses[count].resp = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
responses[count].resp = null;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*resp = responses;
|
||||||
|
|
||||||
|
return PAM_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
class Auth {
|
||||||
|
int authenticate(string pamod, string username, string password) {
|
||||||
|
if (!username.length) {
|
||||||
|
log.e("%s:%d: Username cannot be empty".format(__FUNCTION__, __LINE__));
|
||||||
|
return AUTH_ERR_USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!password.length) {
|
||||||
|
log.e("%s:%d: Password cannot be empty".format(__FUNCTION__, __LINE__));
|
||||||
|
return AUTH_ERR_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
PamConv *pamh = null;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
PAMdata data = { password };
|
||||||
|
void *appdata_ptr = &data;
|
||||||
|
|
||||||
|
PamConv conv = { cast(conversation*)&conversation_func, appdata_ptr };
|
||||||
|
|
||||||
|
retval = pam_start(pamod.toStringz, username.toStringz, &conv, &pamh);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
log.e("%s:%d: %s".format(__FUNCTION__, __LINE__, pam_strerror(pamh, retval).to!string));
|
||||||
|
return AUTH_ERR_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = pam_authenticate(pamh, 0);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
log.e("%s:%d: %s".format(__FUNCTION__, __LINE__, pam_strerror(pamh, retval).to!string));
|
||||||
|
pam_end(pamh, retval);
|
||||||
|
return AUTH_ERR_AUTH;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = pam_end(pamh, PAM_SUCCESS);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
log.e("%s:%d: %s".format(__FUNCTION__, __LINE__, pam_strerror(pamh, retval).to!string));
|
||||||
|
return AUTH_ERR_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AUTH_SUCCESS;
|
||||||
|
}
|
||||||
|
|
||||||
|
int changePassword(string pamod, string username, string password, string newPassword) {
|
||||||
|
if (!username.length) {
|
||||||
|
return AUTH_ERR_USER;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!password.length) {
|
||||||
|
return AUTH_ERR_PASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newPassword.length) {
|
||||||
|
return AUTH_ERR_NPASS;
|
||||||
|
}
|
||||||
|
|
||||||
|
PamConv *pamh = null;
|
||||||
|
int retval = 0;
|
||||||
|
|
||||||
|
PAMdata data = { password, newPassword };
|
||||||
|
void *appdata_ptr = &data;
|
||||||
|
|
||||||
|
PamConv conv = { cast(conversation*)&conversation_func, appdata_ptr };
|
||||||
|
|
||||||
|
retval = pam_start(pamod.toStringz, username.toStringz, &conv, &pamh);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
log.e("%s:%d: %s".format(__FUNCTION__, __LINE__, pam_strerror(pamh, retval).to!string));
|
||||||
|
return AUTH_ERR_START;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = pam_acct_mgmt(pamh, 0);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
log.e("%s:%d: %s".format(__FUNCTION__, __LINE__, pam_strerror(pamh, retval).to!string));
|
||||||
|
pam_end(pamh, retval);
|
||||||
|
return AUTH_ERR_ACCT;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = pam_chauthtok(pamh, 0);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
log.e("%s:%d: %s".format(__FUNCTION__, __LINE__, pam_strerror(pamh, retval).to!string));
|
||||||
|
pam_end(pamh, retval);
|
||||||
|
return AUTH_ERR_CHTOK;
|
||||||
|
}
|
||||||
|
|
||||||
|
retval = pam_end(pamh, PAM_SUCCESS);
|
||||||
|
if (retval != PAM_SUCCESS) {
|
||||||
|
log.e("%s:%d: %s".format(__FUNCTION__, __LINE__, pam_strerror(pamh, retval).to!string));
|
||||||
|
return AUTH_ERR_END;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AUTH_SUCCESS;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module chkpass.user;
|
||||||
|
|
||||||
|
public import chkpass.user.auth;
|
|
@ -0,0 +1,30 @@
|
||||||
|
module chkpass.utils.config;
|
||||||
|
|
||||||
|
import std.file;
|
||||||
|
import readconf;
|
||||||
|
import singlog;
|
||||||
|
|
||||||
|
struct Settings {
|
||||||
|
string check = "login";
|
||||||
|
string change = "passwd";
|
||||||
|
}
|
||||||
|
|
||||||
|
Settings readConfigFile(string configFile) {
|
||||||
|
Settings settings;
|
||||||
|
|
||||||
|
if (configFile.exists) {
|
||||||
|
rc.read(configFile);
|
||||||
|
|
||||||
|
ConfigSection mainSection;
|
||||||
|
|
||||||
|
try {
|
||||||
|
mainSection = rc[][];
|
||||||
|
if (!mainSection.key("check").empty) settings.check = mainSection.key("check");
|
||||||
|
if (!mainSection.key("change").empty) settings.change = mainSection.key("change");
|
||||||
|
} catch (Exception e) {
|
||||||
|
log.w("An error occurred while reading the configuration file");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return settings;
|
||||||
|
}
|
|
@ -0,0 +1,3 @@
|
||||||
|
module chkpass.utils;
|
||||||
|
|
||||||
|
public import chkpass.utils.config;
|
|
@ -0,0 +1,3 @@
|
||||||
|
module chkpass.version_;
|
||||||
|
|
||||||
|
enum chkpassVersion = "0.1.0";
|
Loading…
Reference in New Issue