diff --git a/res/config.lua b/res/config.lua index e0fc47a..d233fbb 100644 --- a/res/config.lua +++ b/res/config.lua @@ -262,6 +262,13 @@ ly = { -- Default is red and bold error_fg = 0x01FF0000, + -- Tally directory for the pam_faillock module (if present) + -- Used for getting if an account is locked or not after too many failed + -- login attempts + -- If directory doesn't exist, lock status won't be checked when + -- authenticating + faillock_tally_dir = "/var/run/faillock", + -- Foreground color id fg = 0x00FFFFFF, diff --git a/res/lang/ar.ini b/res/lang/ar.ini index 009be7c..2c16dcc 100644 --- a/res/lang/ar.ini +++ b/res/lang/ar.ini @@ -6,6 +6,7 @@ custom = مخصص custom_info_err_output_long = الإخراج طويل جداً custom_info_err_no_output = لا يوجد إخراج custom_info_err_no_output_error = ، خطأ محتمل + err_alloc = فشل في تخصيص الذاكرة err_args = تعذر تحليل وسيطات سطر الأوامر err_autologin_session = لم يتم العثور على جلسة تسجيل الدخول التلقائي diff --git a/res/lang/bg.ini b/res/lang/bg.ini index 66cba42..f5540cb 100644 --- a/res/lang/bg.ini +++ b/res/lang/bg.ini @@ -6,6 +6,7 @@ custom = персонализирано custom_info_err_output_long = резултатът е твърде дълъг custom_info_err_no_output = няма резултат custom_info_err_no_output_error = , възможна грешка + err_alloc = неуспешно заделяне на памет err_args = неуспешен анализ на аргументите от командния ред err_autologin_session = сесията за автоматично влизане не е намерена diff --git a/res/lang/cat.ini b/res/lang/cat.ini index f8684fb..02a501f 100644 --- a/res/lang/cat.ini +++ b/res/lang/cat.ini @@ -6,6 +6,7 @@ custom = personalitzat custom_info_err_output_long = sortida massa llarga custom_info_err_no_output = sense sortida custom_info_err_no_output_error = , possible error + err_alloc = assignació de memòria fallida err_args = no s'han pogut analitzar els arguments de la línia d'ordres err_autologin_session = no s'ha trobat la sessió d'inici de sessió automàtic diff --git a/res/lang/cs.ini b/res/lang/cs.ini index 3e5683e..a8eb2bc 100644 --- a/res/lang/cs.ini +++ b/res/lang/cs.ini @@ -6,6 +6,7 @@ custom = vlastní custom_info_err_output_long = výstup je příliš dlouhý custom_info_err_no_output = žádný výstup custom_info_err_no_output_error = , možná chyba + err_alloc = alokace paměti selhala err_args = nelze analyzovat argumenty příkazového řádku err_autologin_session = relace automatického přihlášení nebyla nalezena diff --git a/res/lang/de.ini b/res/lang/de.ini index 18e8a29..fcaf6a2 100644 --- a/res/lang/de.ini +++ b/res/lang/de.ini @@ -6,6 +6,7 @@ custom = benutzerdefiniert custom_info_err_output_long = Ausgabe zu lang custom_info_err_no_output = keine Ausgabe custom_info_err_no_output_error = , möglicher Fehler + err_alloc = Speicherzuweisung fehlgeschlagen err_args = Kommandozeilenargumente konnten nicht verarbeitet werden err_autologin_session = Autologin-Sitzung nicht gefunden diff --git a/res/lang/en.ini b/res/lang/en.ini index 7ad3039..e6f166e 100644 --- a/res/lang/en.ini +++ b/res/lang/en.ini @@ -6,6 +6,7 @@ custom = custom custom_info_err_output_long = output too long custom_info_err_no_output = no output custom_info_err_no_output_error = , possible error +err_acc_locked = account locked, too many attempts err_alloc = failed memory allocation err_args = unable to parse command line arguments err_autologin_session = autologin session not found diff --git a/res/lang/eo.ini b/res/lang/eo.ini index 4e18590..ebe431c 100644 --- a/res/lang/eo.ini +++ b/res/lang/eo.ini @@ -6,6 +6,7 @@ custom = propra custom_info_err_output_long = eligo tro longa custom_info_err_no_output = neniu eligo custom_info_err_no_output_error = , ebla eraro + err_alloc = malsukcesis memorasignon err_args = ne povas analizi argumentojn de komanda linio err_autologin_session = aŭtomatan ensalutan seancon ne trovis diff --git a/res/lang/es.ini b/res/lang/es.ini index 7565ec4..bdb3615 100644 --- a/res/lang/es.ini +++ b/res/lang/es.ini @@ -6,6 +6,7 @@ custom = personalizado custom_info_err_output_long = salida demasiado larga custom_info_err_no_output = sin salida custom_info_err_no_output_error = , posible error + err_alloc = asignación de memoria fallida err_args = no se pudieron analizar los argumentos de la línea de comandos err_autologin_session = no se encontró la sesión de inicio de sesión automático diff --git a/res/lang/fr.ini b/res/lang/fr.ini index 167645c..b2f9651 100644 --- a/res/lang/fr.ini +++ b/res/lang/fr.ini @@ -6,6 +6,7 @@ custom = customisé custom_info_err_output_long = sortie trop longue custom_info_err_no_output = pas de sortie custom_info_err_no_output_error = , erreur possible +err_acc_locked = compte bloqué, trop de tentatives err_alloc = échec d'allocation mémoire err_args = échec de l'analyse des arguments en lignes de commande err_autologin_session = session de connexion automatique introuvable diff --git a/res/lang/it.ini b/res/lang/it.ini index 28889ce..580a44c 100644 --- a/res/lang/it.ini +++ b/res/lang/it.ini @@ -6,6 +6,7 @@ custom = personalizzato custom_info_err_output_long = output troppo lungo custom_info_err_no_output = nessun output custom_info_err_no_output_error = , possibile errore + err_alloc = impossibile allocare memoria err_args = impossibile analizzare gli argomenti della riga di comando err_autologin_session = sessione di accesso automatico non trovata diff --git a/res/lang/ja_JP.ini b/res/lang/ja_JP.ini index 6551af7..b64934b 100644 --- a/res/lang/ja_JP.ini +++ b/res/lang/ja_JP.ini @@ -6,6 +6,7 @@ custom = カスタム custom_info_err_output_long = 出力が長すぎます custom_info_err_no_output = 出力なし custom_info_err_no_output_error = 、エラーの可能性あり + err_alloc = メモリ割り当て失敗 err_args = コマンドライン引数を解析できません err_autologin_session = 自動ログインセッションが見つかりません diff --git a/res/lang/ku.ini b/res/lang/ku.ini index 7fbf777..74cd801 100644 --- a/res/lang/ku.ini +++ b/res/lang/ku.ini @@ -6,6 +6,7 @@ custom = kesane custom_info_err_output_long = encam pir dirêj e custom_info_err_no_output = encam tune custom_info_err_no_output_error = , xeletiya mimkun + err_alloc = veqetandina bîrê têk çû err_args = argumanên rêzika fermanê nehatin analîzkirin err_autologin_session = danişîna têketina xweber nehate dîtin diff --git a/res/lang/lv.ini b/res/lang/lv.ini index 6dc14d1..ee108c9 100644 --- a/res/lang/lv.ini +++ b/res/lang/lv.ini @@ -6,6 +6,7 @@ custom = pielāgots custom_info_err_output_long = izvade pārāk gara custom_info_err_no_output = nav izvades custom_info_err_no_output_error = , iespējama kļūda + err_alloc = neizdevās atmiņas piešķiršana err_args = nevar parsēt komandrindas argumentus err_autologin_session = automātiskās pieteikšanās sesija nav atrasta diff --git a/res/lang/pl.ini b/res/lang/pl.ini index 0910ee7..dc2770b 100644 --- a/res/lang/pl.ini +++ b/res/lang/pl.ini @@ -6,6 +6,7 @@ custom = własny custom_info_err_output_long = wyjście zbyt długie custom_info_err_no_output = brak wyjścia custom_info_err_no_output_error = , możliwy błąd + err_alloc = nieudana alokacja pamięci err_args = nie można przetworzyć argumentów wiersza poleceń err_autologin_session = nie znaleziono sesji autologowania diff --git a/res/lang/pt.ini b/res/lang/pt.ini index 2b0ace4..5ea0f9f 100644 --- a/res/lang/pt.ini +++ b/res/lang/pt.ini @@ -6,6 +6,7 @@ custom = personalizado custom_info_err_output_long = saída demasiado longa custom_info_err_no_output = sem saída custom_info_err_no_output_error = , possível erro + err_alloc = erro na atribuição de memória err_args = não foi possível analisar os argumentos da linha de comandos err_autologin_session = sessão de início de sessão automático não encontrada diff --git a/res/lang/pt_BR.ini b/res/lang/pt_BR.ini index f79bb71..1fee82e 100644 --- a/res/lang/pt_BR.ini +++ b/res/lang/pt_BR.ini @@ -6,6 +6,7 @@ custom = personalizado custom_info_err_output_long = saída muito longa custom_info_err_no_output = sem saída custom_info_err_no_output_error = , possível erro + err_alloc = alocação de memória malsucedida err_args = não foi possível analisar os argumentos da linha de comando err_autologin_session = sessão de login automático não encontrada diff --git a/res/lang/ro.ini b/res/lang/ro.ini index 318889f..a733101 100644 --- a/res/lang/ro.ini +++ b/res/lang/ro.ini @@ -6,6 +6,7 @@ custom = personalizat custom_info_err_output_long = ieșire prea lungă custom_info_err_no_output = fără ieșire custom_info_err_no_output_error = , posibil eroare + err_alloc = alocare de memorie eșuată err_args = imposibil de analizat argumentele liniei de comandă err_autologin_session = sesiunea de autentificare automată nu a fost găsită diff --git a/res/lang/ru.ini b/res/lang/ru.ini index 359dd95..1da01da 100644 --- a/res/lang/ru.ini +++ b/res/lang/ru.ini @@ -6,6 +6,7 @@ custom = пользовательский custom_info_err_output_long = вывод слишком длинный custom_info_err_no_output = нет вывода custom_info_err_no_output_error = , возможная ошибка + err_alloc = не удалось выделить память err_args = не удалось разобрать аргументы командной строки err_autologin_session = не найдена сессия с автологином diff --git a/res/lang/sr.ini b/res/lang/sr.ini index e82c4fa..fdf32a2 100644 --- a/res/lang/sr.ini +++ b/res/lang/sr.ini @@ -6,6 +6,7 @@ custom = prilagođeno custom_info_err_output_long = izlaz predugačak custom_info_err_no_output = nema izlaza custom_info_err_no_output_error = , moguća greška + err_alloc = neuspješna alokacija memorije err_args = nije moguće raščlaniti argumente komandne linije err_autologin_session = sesija automatske prijave nije pronađena diff --git a/res/lang/sr_Cyrl.ini b/res/lang/sr_Cyrl.ini index 2e869bb..0cb3944 100644 --- a/res/lang/sr_Cyrl.ini +++ b/res/lang/sr_Cyrl.ini @@ -6,6 +6,7 @@ custom = прилагођено custom_info_err_output_long = излаз предугачак custom_info_err_no_output = нема излаза custom_info_err_no_output_error = , могућа грешка + err_alloc = неуспешна алокација меморије err_args = није могуће рашчланити аргументе командне линије err_autologin_session = сесија аутоматске пријаве није пронађена diff --git a/res/lang/sv.ini b/res/lang/sv.ini index 24fb6c9..58376b8 100644 --- a/res/lang/sv.ini +++ b/res/lang/sv.ini @@ -6,6 +6,7 @@ custom = anpassad custom_info_err_output_long = utdata för lång custom_info_err_no_output = ingen utdata custom_info_err_no_output_error = , möjligt fel + err_alloc = minnesallokering misslyckades err_args = tolkning av kommandoargument misslyckades err_autologin_session = autologin-session hittades inte diff --git a/res/lang/tr.ini b/res/lang/tr.ini index a0d344f..fde531d 100644 --- a/res/lang/tr.ini +++ b/res/lang/tr.ini @@ -6,6 +6,7 @@ custom = özel custom_info_err_output_long = çıktı çok uzun custom_info_err_no_output = çıktı yok custom_info_err_no_output_error = , olası hata + err_alloc = basarisiz bellek ayirma err_args = komut satırı argümanları ayrıştırılamıyor err_autologin_session = otomatik oturum açma oturumu bulunamadı diff --git a/res/lang/uk.ini b/res/lang/uk.ini index b096b9a..fbd5b6d 100644 --- a/res/lang/uk.ini +++ b/res/lang/uk.ini @@ -6,6 +6,7 @@ custom = власний custom_info_err_output_long = вивід занадто довгий custom_info_err_no_output = немає виводу custom_info_err_no_output_error = , можлива помилка + err_alloc = невдале виділення пам'яті err_args = не вдалося розібрати аргументи командного рядка err_autologin_session = сеанс автоматичного входу не знайдено diff --git a/res/lang/zh_CN.ini b/res/lang/zh_CN.ini index 183ec89..21f31ee 100644 --- a/res/lang/zh_CN.ini +++ b/res/lang/zh_CN.ini @@ -6,6 +6,7 @@ custom = 自定义 custom_info_err_output_long = 输出过长 custom_info_err_no_output = 无输出 custom_info_err_no_output_error = ,可能有错误 + err_alloc = 内存分配失败 err_args = 无法解析命令行参数 err_autologin_session = 未找到自动登录会话 diff --git a/res/lang/zh_TW.ini b/res/lang/zh_TW.ini index 606b2a1..ee397c1 100644 --- a/res/lang/zh_TW.ini +++ b/res/lang/zh_TW.ini @@ -6,6 +6,7 @@ custom = 自訂 custom_info_err_output_long = 輸出過長 custom_info_err_no_output = 無輸出 custom_info_err_no_output_error = ,可能有錯誤 + err_alloc = 記憶體配置失敗 err_args = 無法解析命令列參數 err_autologin_session = 找不到自動登入工作階段 diff --git a/src/auth.zig b/src/auth.zig index 91f1cbc..28778b8 100644 --- a/src/auth.zig +++ b/src/auth.zig @@ -20,6 +20,7 @@ pub const AuthOptions = struct { xauth_cmd: []const u8, setup_cmd: []const u8, login_cmd: ?[]const u8, + faillock_tally_dir: []const u8, x_cmd: []const u8, x_vt: ?u8, session_pid: std.posix.pid_t, @@ -34,6 +35,18 @@ const PamAppdata = struct { new_password: []const u8, }; +//https://github.com/linux-pam/linux-pam/blob/master/modules/pam_faillock/faillock.h#L55 +const PamFaillockEntry = extern struct { + pub const STATUS_VALID: usize = 0x1; + pub const STATUS_RHOST: usize = 0x2; + pub const STATUS_TTY: usize = 0x4; + + source: [52]u8, + reserved: u16, + status: u16, + time: u64, +}; + var xorg_pid: std.posix.pid_t = 0; pub fn xorgSignalHandler(sig: std.posix.SIG) callconv(.c) void { if (xorg_pid > 0) _ = std.c.kill(xorg_pid, sig); @@ -54,6 +67,11 @@ pub fn authenticate( password: []const u8, maybe_new_password: ?[]const u8, ) !void { + var faillock_entries: usize = 0; + if (try dirExists(io, options.faillock_tally_dir)) { + faillock_entries = try getFaillockEntries(allocator, io, login, options.faillock_tally_dir); + } + var tty_buffer: [3]u8 = undefined; const tty_str = try std.fmt.bufPrint(&tty_buffer, "{d}", .{options.tty}); @@ -94,6 +112,13 @@ pub fn authenticate( // Do the PAM routine try log_file.info(io, "auth/pam", "authenticating", .{}); status = interop.pam.pam_authenticate(handle, 0); + if (status == interop.pam.PAM_AUTH_ERR and try dirExists(io, options.faillock_tally_dir)) { + const new_faillock_entries = try getFaillockEntries(allocator, io, login, options.faillock_tally_dir); + + if (faillock_entries == new_faillock_entries) { + return error.AccountLocked; + } + } if (status != interop.pam.PAM_SUCCESS) return pamDiagnose(status); try log_file.info(io, "auth/pam", "validating account", .{}); @@ -188,6 +213,46 @@ pub fn authenticate( if (shared_err.readError()) |err| return err; } +fn dirExists(io: std.Io, path: []const u8) !bool { + var dir = std.Io.Dir.openDirAbsolute(io, path, .{}) catch |err| { + if (err == error.FileNotFound) return false; + return err; + }; + defer dir.close(io); + + return true; +} + +fn getFaillockEntries( + allocator: std.mem.Allocator, + io: std.Io, + username: []const u8, + tally_dir: []const u8, +) !usize { + const path = try std.fs.path.join(allocator, &.{ tally_dir, username }); + defer allocator.free(path); + + var file = try std.Io.Dir.openFileAbsolute(io, path, .{}); + defer file.close(io); + + var buffer: [1024]u8 = undefined; + var reader = file.reader(io, &buffer); + var count: usize = 0; + + while (!reader.atEnd()) { + const entry = reader.interface.takeStruct(PamFaillockEntry, .little) catch |err| { + if (err == error.EndOfStream) break; + return err; + }; + + if (entry.status & PamFaillockEntry.STATUS_VALID != 0) { + count += 1; + } + } + + return count; +} + fn startSession( log_file: *LogFile, allocator: std.mem.Allocator, diff --git a/src/config/Config.zig b/src/config/Config.zig index 18d4abe..463a9cf 100644 --- a/src/config/Config.zig +++ b/src/config/Config.zig @@ -59,6 +59,7 @@ dur_y_offset: i32 = 0, edge_margin: u8 = 0, error_bg: u32 = 0x00000000, error_fg: u32 = 0x01FF0000, +faillock_tally_dir: []const u8 = "/var/run/faillock", fg: u32 = 0x00FFFFFF, full_color: bool = true, gameoflife_fg: u32 = 0x0000FF00, diff --git a/src/config/Lang.zig b/src/config/Lang.zig index d2a97e2..85051aa 100644 --- a/src/config/Lang.zig +++ b/src/config/Lang.zig @@ -11,6 +11,7 @@ custom: []const u8 = "custom", custom_info_err_output_long: []const u8 = "output too long", custom_info_err_no_output: []const u8 = "no output", custom_info_err_no_output_error: []const u8 = ", possible error", +err_acc_locked: []const u8 = "account locked, too many attempts", err_alloc: []const u8 = "failed memory allocation", err_args: []const u8 = "unable to parse command line arguments", err_autologin_session: []const u8 = "autologin session not found", diff --git a/src/main.zig b/src/main.zig index 3ceebd2..4d6bb65 100644 --- a/src/main.zig +++ b/src/main.zig @@ -1614,6 +1614,7 @@ fn authenticate(ptr: *anyopaque) !bool { .xauth_cmd = state.config.xauth_cmd, .setup_cmd = state.config.setup_cmd, .login_cmd = state.config.login_cmd, + .faillock_tally_dir = state.config.faillock_tally_dir, .x_cmd = state.config.x_cmd, .x_vt = state.config.x_vt, .session_pid = session_pid, @@ -2641,6 +2642,7 @@ fn getAuthErrorMsg(err: anyerror, lang: Lang) []const u8 { error.PamSystemError => lang.err_pam_sys, error.PamUserUnknown => lang.err_pam_user_unknown, error.PamAbort => lang.err_pam_abort, + error.AccountLocked => lang.err_acc_locked, else => @errorName(err), }; }