Compare commits

...

74 commits

Author SHA1 Message Date
cb8edf2913 README update. 2026-01-13 00:20:45 +01:00
fd829f1569 Some content update... 2025-09-08 03:59:43 +02:00
37a10d5c07 Some content update... 2025-09-08 03:49:31 +02:00
5a092b3b65 Some content update... 2025-09-08 03:37:36 +02:00
b082f1592b Some content update... 2025-09-08 03:35:08 +02:00
7adcaf989f Some fix. 2025-09-08 03:32:40 +02:00
025f792261 Some content update... 2025-09-08 03:17:18 +02:00
9709333e76 Some content update and fixes... 2025-09-08 03:13:19 +02:00
22303a3506 Some content update and fixes... 2025-09-08 03:08:28 +02:00
f9e1cf4c05 Some content update and fixes... 2025-09-08 03:05:56 +02:00
92fdb05161 Some content update and fixes... 2025-09-08 02:55:20 +02:00
71555fc275 Merged chkpwr to chkusr and moved defines to config.h. 2025-09-08 02:34:46 +02:00
0957057793 README update. 2025-06-27 03:10:35 +02:00
eb69c6989c README update. 2025-06-27 03:09:43 +02:00
d92c2d2a76 Merge nearly ready for 1.4.0. 2025-06-27 02:56:55 +02:00
f8870f6c80 Cleanups. 2025-06-27 02:30:01 +02:00
45973a3a90 README update. 2025-06-27 01:32:23 +02:00
12ea8e8273 README update. 2025-06-27 01:30:22 +02:00
bf732541c0 README update. 2025-06-27 01:09:31 +02:00
8644ccbdc5 Merged with chkgrp and renamed to chkusr. 2025-06-27 00:57:42 +02:00
b2461ba7a0 Link update as the site used Google adds. 2025-06-05 20:46:08 +02:00
dbd240f31f Just some small fixes and cosmetics... (1.3.1) 2025-06-05 20:23:53 +02:00
ccfd170bae Fixed man page. 2025-06-05 20:07:03 +02:00
ce7fc820a7 Added links to README. 2025-06-05 04:23:43 +02:00
3d0022ced1 Typo fix. 2025-06-05 01:51:44 +02:00
d819e7c821 gitignore fix. 2025-06-04 19:06:31 +02:00
a2bcc55b38 Renamed chkpw to chkpwd. (1.3.0) 2025-06-04 18:43:26 +02:00
0fb2bc12e7 Added link to unix_chkpwd man page. 2025-06-04 00:25:02 +02:00
bdc6fab467 I am stupid... :) 2025-06-01 19:27:19 +02:00
22e4f0790a Renamed project from checkpw to chkpw. (1.2.0) 2025-06-01 19:23:47 +02:00
1665cdc1a3 README fix. 2025-05-30 04:18:12 +02:00
bec3be6746 Added requirements to README. 2025-05-30 04:17:15 +02:00
4c50974efc README update. 2025-03-10 00:51:40 +01:00
6d3e3393b5 Some housekeeping... 2025-01-11 04:42:45 +01:00
687777f90b Man page version fix... 2025-01-10 02:57:12 +01:00
bc4713e82e Man page version fix. 2025-01-10 02:55:42 +01:00
0b7219c6d8 Moved defines from .h to .c. These are not used in the .h file. 2025-01-10 02:54:20 +01:00
5e80936967 Made the Makefile more generic and .gitignore update. 2025-01-01 10:59:44 +01:00
bd2ac5b829 Makefile update. I like absolute paths... ;) 2025-01-01 10:28:34 +01:00
f53aaced69 Makefile fix. 2025-01-01 09:33:15 +01:00
ae586d053b Version update to 1.1.1. 2025-01-01 09:17:52 +01:00
66d7b352d6 Makefile fix. 2025-01-01 09:05:56 +01:00
84d481aa6f Added man page to Makefile and README fixes. 2025-01-01 08:54:26 +01:00
ec9b6c38ae Added basic man page. 2025-01-01 08:43:08 +01:00
e0a32fcedc README update. 2025-01-01 08:13:06 +01:00
27bfe141d1 README fix. 2025-01-01 07:31:22 +01:00
23d4719078 README fix. 2025-01-01 07:19:45 +01:00
4e0bd0df75 README update. 2025-01-01 06:51:41 +01:00
6ee75766b0 README update. 2025-01-01 06:46:29 +01:00
d7780a2d41 Added license to README. 2025-01-01 06:43:23 +01:00
82aced57b6 Removed interactive mode (is default now), moved some code to checkpw.h and many fixes. 2025-01-01 06:23:03 +01:00
6f859b18b2 License change from MIT to Apache-2.0. 2024-12-24 00:40:01 +01:00
70550ffa7f README update. 2024-12-12 00:20:22 +01:00
5bd3572c75 Code style fixes. 2024-12-12 00:17:22 +01:00
97d9abd6c1 Some code-style fixes. 2024-10-29 06:45:35 +01:00
root
a771e28f3b Added some newlines to help output. 2024-10-29 06:36:16 +01:00
7b0d7ce7a7 Fix applied. 2024-09-26 00:26:34 +02:00
2cc8792452 README update. 2024-09-25 23:09:41 +02:00
04ad31fb2e README update. 2024-09-25 06:31:16 +02:00
60919c1ca8 README update. 2024-09-25 06:30:21 +02:00
7ffe30647b README update. 2024-09-24 04:50:29 +02:00
1ee8e07b0a README update. 2024-09-24 04:48:06 +02:00
e7b7397c93 Changed -Werror to -Wall in Makefile and README.md. 2024-08-28 18:18:57 +02:00
4efe3efa96 README update. 2024-08-28 18:16:59 +02:00
689d1cbe18 README fix. 2024-08-28 02:24:15 +02:00
f0b2352975 Mirror to GitHub repo fix. 2024-08-28 02:09:30 +02:00
a1f63c428c Added -V to show the program version. Updated to 1.0.3. 2024-08-28 02:05:27 +02:00
1311e992f8 Updated version to 1.0.2. 2024-08-27 21:07:44 +02:00
5115ad76d9 Added Link. Btw. commit @98249359c5 was the fix for Musl libc. 2024-08-27 21:04:25 +02:00
d5a27484d0 Added Uninstall section README. Btw. the last commit fixed compile errors using Musl libc. 2024-08-27 20:27:17 +02:00
03afded918 Added checkpw.o to .gitignore. 2024-08-27 20:23:41 +02:00
98249359c5 Fixed compile error on Debian based systems. 2024-08-27 20:12:59 +02:00
c4caadde93 Added Links to README.md. 2024-08-20 17:52:42 +02:00
5a992efb7e README.md update. 2024-08-18 04:08:06 +02:00
20 changed files with 1352 additions and 304 deletions

15
.gitignore vendored
View file

@ -1,2 +1,13 @@
checkpw *.o
*.so
chkgrp
chkgrp-min
chkgrp-native
chkgrp-native-min
chkgrp-native-min-extended
chkgrp-ng
chkpwd
chkpwd.1.gz
chkpwr
test
test.c

16
LICENSE
View file

@ -1,9 +1,13 @@
MIT License Copyright 2024 Johannes Findeisen
Copyright (c) 2024 hanez Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: http://www.apache.org/licenses/LICENSE-2.0
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -1,12 +1,52 @@
all: all:
$(CC) -Werror -lpam -lpam_misc -o checkpw checkpw.c $(CC) -Wall -o ./chkgrp ./chkgrp.c
$(CC) -Wall -o ./chkgrp-min ./chkgrp-min.c
$(CC) -Wall -o ./chkgrp-native ./chkgrp-native.c
$(CC) -Wall -o ./chkgrp-native-min ./chkgrp-native-min.c
$(CC) -Wall -o ./chkgrp-native-min-extended ./chkgrp-native-min-extended.c
$(CC) -Wall -o ./chkgrp-ng ./chkgrp-ng.c
$(CC) -Wall -o ./chkpwd ./chkpwd.c -lpam
gzip -fk ./chkpwd.1
$(CC) -Wall -o ./chkpwr ./chkpwr.c
clean: clean:
rm -f ./checkpw rm -f ./chkgrp
rm -f ./chkgrp-min
rm -f ./chkgrp-native
rm -f ./chkgrp-native-min
rm -f ./chkgrp-native-min-extended
rm -f ./chkgrp-ng
rm -f ./chkpwd
rm -f ./chkpwd.1.gz
rm -f ./chkpwr
rm -f ./test
install: install:
cp ./checkpw /usr/bin/ install -g 0 -o 0 -m 0655 ./chkgrp /usr/bin/
install -g 0 -o 0 -m 0655 ./chkgrp-min /usr/bin/
install -g 0 -o 0 -m 0655 ./chkgrp-native /usr/bin/
install -g 0 -o 0 -m 0655 ./chkgrp-native-min /usr/bin/
install -g 0 -o 0 -m 0655 ./chkgrp-native-min-extended /usr/bin/
install -g 0 -o 0 -m 0655 ./chkgrp-ng /usr/bin/
install -g 0 -o 0 -m 0655 ./chkpwd /usr/bin/
install -g 0 -o 0 -m 0644 ./chkpwd.h /usr/include/
install -g 0 -o 0 -m 0644 ./chkpwd.1.gz /usr/share/man/man1/
install -g 0 -o 0 -m 0655 ./chkpwr /usr/bin/
install -g 0 -o 0 -m 0655 ./lua/chkgrp-native-min-extended.lua /usr/bin/
uninstall: uninstall:
rm -f /usr/bin/checkpw rm -f /usr/bin/chkgrp
rm -f /usr/bin/chkgrp-min
rm -f /usr/bin/chkgrp-native
rm -f /usr/bin/chkgrp-native-min
rm -f /usr/bin/chkgrp-native-min-extended
rm -f /usr/bin/chkgrp-native-min-extended.lua
rm -f /usr/bin/chkgrp-ng
rm -f /usr/bin/chkpwd
rm -f /usr/bin/chkpwr
rm -f /usr/include/chkpwd.h
rm -f /usr/share/man/man1/chkpwd.1.gz
test:
$(CC) -Wall -DPAM_DEBUG -o test test.c

150
README.md
View file

@ -1,72 +1,162 @@
# checkpw - 1.0.1! # chkusr
checkpw is a program that checks the validity of a users password on a Linux/PAM-based system. chkusr is a set of programs (chkgrp, chkpwd and chkpwr) to verify information about a user and/or group on UNIX based systems.
## The idea behind: ## THE FOLLOWING DOCUMENTATION WILL NOT WORK ACTUALLY! YOU NEED TO READ THE CODE! SORRY...
I needed a program to verify passwords of users on Linux based systems using PAM. * [The idea](#the-idea)
* [Requirements](#requirements)
* [Configuration](#configuration)
* [Building chkusr](#building-chkusr)
* [Installation](#installation)
* [Uninstall](#uninstall)
* [Programs](#programs)
* [chkgrp](#chkgrp)
* [chkpwd](#chkpwd)
* [Usage](#usage)
* [Return codes](#return-codes)
* [Examples](#examples)
* [Interactive mode asking for a username and a password](#interactive-mode-asking-for-a-username-and-a-password)
* [Interactive mode only asking for a password](#interactive-mode-only-asking-for-a-password)
* [None interactive mode with username and password provided as arguments to chkpwd](#none-interactive-mode-with-username-and-password-provided-as-arguments-to-chkpwd)
* [Request the result from the above commands](#request-the-result-from-the-above-commands)
* [Custom build](#custom-build)
* [chkpwr](#chkpwr)
* [Links](#links)
* [License](#license)
Exactly a program like this... not more! ### The idea
## Installation: I needed some programs to verify some information about users and groups on a Linux/UNIX system just returning 0 on success and 1 on error. Nonetheless, some programs also return a message.
**WARNING:** Install this software with care. checkpw could easily be used for bruteforcing passwords from local users! #### Requirements
You need the PAM development package installed. On Alpine it is named linux-pam-dev, on Debian based systems it is named libpam0g-dev. Not all programs require PAM but you need to edit the Makefile to disable PAM based programs in the build process.
#### Configuration
chkusr needs to be configured before compilation. Always look at config.h for configuration options. I did it this way because I need the programs as small and simple as possible, and I don't want to parse a configuration file.
#### Building chkusr
``` ```
git clone https://git.xw3.org/hanez/checkpw.git git clone https://git.xw3.org/hanez/chkusr.git
cd checkpw cd chkusr
make make
```
#### Installation
**WARNING:** Install this software with care. chkpwd could easily be used for bruteforcing passwords from local users!
```
sudo make install sudo make install
``` ```
The code only supports verifying passwords for user id 1000 by default. Look a the code for some compile time options! chkusr programs are installed to /usr/bin/.
Set MAX_UID and MIN_UID in the code or you can compile checkpw without editing the code using the following command and install it manually: chkpwd.h is installed to /usr/include/ for use in other applications.
#### Uninstall
``` ```
gcc -Werror -lpam -lpam_misc -DMAX_UID=1000 -DMIN_UID=1000 -o checkpw checkpw.c sudo make uninstall
sudo cp ./checkpw /usr/bin/
``` ```
## Usage: ### Programs
#### chkgrp
chkgrp is a program to verify if a user is a member of a group.
More information will follow... Actually there are multiple programs with some different behavior. I will write more about this, but for now I will not.
#### chkpwd
chkpwd is a program that checks the validity of a users password on a UNIX/[PAM](https://en.wikipedia.org/wiki/Pluggable_Authentication_Module)-based system.
Currently chkpwd is only tested on Linux, but it should work on a [AIX](https://en.wikipedia.org/wiki/IBM_AIX), [DragonFly BSD](https://www.dragonflybsd.org/), [FreeBSD](https://www.freebsd.org/), [HP-UX](https://en.wikipedia.org/wiki/HP-UX), [Linux](https://kernel.org/), [macOS](https://en.wikipedia.org/wiki/MacOS), [NetBSD](https://netbsd.org/) and [Solaris](https://en.wikipedia.org/wiki/Oracle_Solaris) operating system too.
The code only supports verifying passwords for user id 1000 by default. Look at the file chkpwd.h for some compile time options!
##### Usage
``` ```
checkpw -h chkpwd -h
Usage: checkpw [-u <username>] [-p <password>] [-i] [-v] [-h] Usage: chkpwd [-u <username>] [-p <password>] [-v] [-V] [-h]
Options: Options:
-u <username> Specify username. -u <username> Set username.
-p <password> Specify password. -p <password> Set password.
-i Enable interactive mode to prompt for missing username/password.
-v Enable verbose mode. -v Enable verbose mode.
-V Print program version.
-h Show this help. -h Show this help.
``` ```
Returns 0 on success, 1 otherwise. You can also use chkpwd even without installing by just running the following command:
### Examples:
#### Interactive mode:
``` ```
checkpw -i ./chkpwd
``` ```
#### Interactive mode only asking for a password: ##### Return codes
chk returns 0 on success, 1 otherwise.
##### Examples
###### Interactive mode asking for a username and a password
``` ```
checkpw -u hanez -i chkpwd
``` ```
#### None interactive mode with username and password provided as parameters: ###### Interactive mode only asking for a password
``` ```
checkpw -u hanez -p password chkpwd -u hanez
``` ```
#### Request the result from the above commands: ###### None interactive mode with username and password provided as arguments to chkpwd
```
chkpwd -u hanez -p password
```
###### Request the result from the above commands
``` ```
echo $? echo $?
``` ```
##### Custom build
Set MAX_UID and MIN_UID at compile time:
```
gcc -Wall -DMAX_UID=1000 -DMIN_UID=1000 -o chkpwd chkpwd.c -lpam -lpam_misc
```
#### chkpwr
A program that checks if an input string validates a password rule. More to come... look at the code!
### Links
- [https://git.xw3.org/hanez/chkusr](https://git.xw3.org/hanez/chkusr)
- [https://github.com/shadow-maint/shadow](https://github.com/shadow-maint/shadow)
- [https://github.com/linux-pam/linux-pam](https://github.com/linux-pam/linux-pam)
- [https://www.man7.org/linux/man-pages/man8/unix_chkpwd.8.html](https://www.man7.org/linux/man-pages/man8/unix_chkpwd.8.html)
- [https://cr.yp.to/checkpwd.html](https://cr.yp.to/checkpwd.html)
- [https://pamtester.sourceforge.net/](https://pamtester.sourceforge.net/)
- [https://github.com/AlexanderZhirov/chkpass](https://github.com/AlexanderZhirov/chkpass)
- [https://github.com/ViKingIX/pam_test](https://github.com/ViKingIX/pam_test)
- [https://github.com/Dareka826/chk_pw](https://github.com/Dareka826/chk_pw)
### License
chkusr is licensed under the Apache License, Version 2.0.
See LICENSE for details.

264
checkpw.c
View file

@ -1,264 +0,0 @@
/**
* checkpw is a program that checks the validity of a users password on a
* Linux/PAM-based system.
*
* Usage: checkpw [-u <username>] [-p <password>] [-i] [-v] [-h]
*
* Options:
* -u <username> Specify username.
* -p <password> Specify password.
* -i Enable interactive mode to prompt for missing username/password.
* -v Enable verbose mode.
* -h Show this help.
*
* Returns 0 on success, 1 otherwise.
*
* Author: Johannes Findeisen <you@hanez.org>
* License: MIT (see LICENSE)
*/
#include <pwd.h> // For struct passwd and getpwnam
#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h> // For terminal input settings
#include <unistd.h> // For getopt and access to user info
#define MAX_USERNAME_LEN 32
#define MAX_PASSWORD_LEN 256
#ifndef MAX_UID
#define MAX_UID 1000
#endif
#ifndef MIN_UID
#define MIN_UID 1000
#endif
// Custom data structure to hold user-entered password
struct pam_credentials {
const char *password;
};
// PAM conversation function to supply the password
int pam_conversation(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr) {
struct pam_response *response = NULL;
struct pam_credentials *credentials = (struct pam_credentials *)appdata_ptr;
int i;
response = (struct pam_response *)malloc(sizeof(struct pam_response) * num_msg);
if (response == NULL)
return PAM_CONV_ERR;
for (i = 0; i < num_msg; i++) {
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_ON:
case PAM_PROMPT_ECHO_OFF:
response[i].resp = strdup(credentials->password);
response[i].resp_retcode = 0;
break;
default:
free(response);
return PAM_CONV_ERR;
}
}
*resp = response;
return PAM_SUCCESS;
}
int authenticate(const char *username, const char *password, int verbose) {
int retval;
pam_handle_t *pamh = NULL;
struct pam_credentials credentials = { password };
struct pam_conv conv = { pam_conversation, &credentials };
if (verbose)
printf("Starting PAM authentication for user '%s'.\n", username);
retval = pam_start("login", username, &conv, &pamh);
if (retval == PAM_SUCCESS) {
if (verbose)
printf("PAM authentication initialized.\n");
retval = pam_authenticate(pamh, 0); // Attempt to authenticate
} else {
if (verbose)
printf("pam_start failed: %s\n", pam_strerror(pamh, retval));
}
if (retval == PAM_SUCCESS) {
if (verbose)
printf("User '%s' authenticated successfully.\n", username);
retval = pam_acct_mgmt(pamh, 0); // Check account validity
if (retval != PAM_SUCCESS && verbose)
printf("pam_acct_mgmt failed: %s\n", pam_strerror(pamh, retval));
} else {
if (verbose)
printf("pam_authenticate failed: %s\n", pam_strerror(pamh, retval));
}
if (pam_end(pamh, retval) != PAM_SUCCESS) {
pamh = NULL;
fprintf(stderr, "Failed to release PAM authenticator\n");
exit(1);
}
if (retval != PAM_SUCCESS && verbose) {
printf("Authentication failed for user '%s'.\n", username);
}
return (retval == PAM_SUCCESS ? 0 : 1); // 0 for success, 1 for failure
}
// Function to prompt user for input, optionally hiding input
void prompt_for_input(char *buffer, size_t size, const char *prompt, int hide_input) {
printf("%s", prompt);
fflush(stdout);
if (hide_input) {
struct termios oldt, newt;
if (tcgetattr(STDIN_FILENO, &oldt) != 0) {
perror("tcgetattr");
exit(1);
}
newt = oldt;
newt.c_lflag &= ~(ECHO);
if (tcsetattr(STDIN_FILENO, TCSANOW, &newt) != 0) {
perror("tcsetattr");
exit(1);
}
}
if (fgets(buffer, size, stdin) == NULL) {
fprintf(stderr, "\nError reading input.\n");
if (hide_input) {
// Restore terminal settings
struct termios oldt;
tcgetattr(STDIN_FILENO, &oldt);
oldt.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
exit(1);
}
// Remove trailing newline
buffer[strcspn(buffer, "\n")] = '\0';
if (hide_input) {
// Restore terminal settings
struct termios oldt;
tcgetattr(STDIN_FILENO, &oldt);
oldt.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
printf("\n"); // Move to the next line after password input
}
}
void print_usage(const char *prog_name) {
fprintf(stderr, "Usage: %s [-u <username>] [-p <password>] [-i] [-v] [-h]\n", prog_name);
fprintf(stderr, "Options:\n");
fprintf(stderr, " -u <username> Specify username.\n");
fprintf(stderr, " -p <password> Specify password.\n");
fprintf(stderr, " -i Enable interactive mode to prompt for missing username/password.\n");
fprintf(stderr, " -v Enable verbose mode.\n");
fprintf(stderr, " -h Show this help.\n");
}
int main(int argc, char *argv[]) {
bool interactive = false;
bool verbose = false;
char username[MAX_USERNAME_LEN] = {0};
char password[MAX_PASSWORD_LEN] = {0};
int opt;
// Parse command-line arguments
while ((opt = getopt(argc, argv, "u:p:hiv")) != -1) {
switch (opt) {
case 'u':
if (strlen(optarg) >= MAX_USERNAME_LEN) {
fprintf(stderr, "Error: Username is too long (maximum %d characters).\n", MAX_USERNAME_LEN);
exit(1);
}
strncpy(username, optarg, MAX_USERNAME_LEN - 1);
break;
case 'p':
if (strlen(optarg) >= MAX_PASSWORD_LEN) {
fprintf(stderr, "Error: Password is too long (maximum %d characters).\n", MAX_PASSWORD_LEN);
exit(1);
}
strncpy(password, optarg, MAX_PASSWORD_LEN - 1);
break;
case 'h':
print_usage(argv[0]);
exit(0);
case 'i':
interactive = true;
break;
case 'v':
verbose = true;
break;
default:
print_usage(argv[0]);
exit(1);
}
}
// If interactive mode is enabled, prompt for missing username and/or password
if (interactive) {
if (username[0] == '\0') {
prompt_for_input(username, sizeof(username), "Username: ", 0);
if (strlen(username) == 0) {
fprintf(stderr, "Error: Username cannot be empty.\n");
exit(1);
}
}
if (password[0] == '\0') {
prompt_for_input(password, sizeof(password), "Password: ", 1);
if (strlen(password) == 0) {
fprintf(stderr, "Error: Password cannot be empty.\n");
exit(1);
}
}
} else {
// If not in interactive mode, ensure username and password are provided
if (username[0] == '\0' || password[0] == '\0') {
fprintf(stderr, "Error: Username and password must be provided unless interactive mode is enabled.\n");
print_usage(argv[0]);
exit(1);
}
}
// Retrieve user information from the username
struct passwd *pwd = getpwnam(username);
if (pwd == NULL) {
fprintf(stderr, "Error: User '%s' not found.\n", username);
exit(1);
}
// Check if the user's UID is below the minimum allowed UID and not higher than maximum allowed UID
if (pwd->pw_uid < MIN_UID || pwd->pw_uid > MAX_UID) {
fprintf(stderr, "Error: User '%s' has a UID less than %d or higher than %d and is not allowed to authenticate.\n", username, MIN_UID, MAX_UID);
exit(1);
}
if (verbose)
printf("User '%s' passed UID check (UID: %d).\n", username, pwd->pw_uid);
// Authenticate the user
if (authenticate(username, password, verbose) == 0) {
printf("Authenticated successfully.\n");
return 0;
} else {
printf("Authentication failed.\n");
return 1;
}
}

48
chkgrp-min.c Normal file
View file

@ -0,0 +1,48 @@
/**
* chkgrp-min is a program that checks if a user is a member of a group.
*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* Homepage: https://git.xw3.org/hanez/chkusr
* License: Apache-2.0 (see LICENSE)
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
int main(int argc, char *argv[])
{
if (argc != 3)
return 2;
if (strlen(argv[1]) > MAX_NAME || strlen(argv[2]) > MAX_NAME)
return 2;
struct passwd *pw = getpwnam(argv[1]);
if (!pw)
return 2;
struct group *gr = getgrnam(argv[2]);
if (!gr)
return 2;
if (pw->pw_gid == gr->gr_gid) {
puts("Yes");
return 0;
}
for (char **m = gr->gr_mem; *m; ++m) {
if (!strcmp(*m, argv[1])) {
puts("Yes");
return 0;
}
}
puts("No");
return 1;
}

View file

@ -0,0 +1,101 @@
/**
* chkgrp-min-native-extended is a program that checks if a user is a member
* of a group.
*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* Homepage: https://git.xw3.org/hanez/chkusr
* License: Apache-2.0 (see LICENSE)
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
if (argc != 3)
return 2;
const char *username = argv[1];
const char *groupname = argv[2];
if (strlen(username) > MAX_NAME || strlen(groupname) > MAX_NAME)
return 2;
// Step 1: Find user in /etc/passwd to get their primary GID
FILE *passwd = fopen("/etc/passwd", "r");
if (!passwd)
return 2;
char line[MAX_LINE];
int user_gid = -1;
while (fgets(line, sizeof(line), passwd)) {
char *u = strtok(line, ":");
if (!u || strcmp(u, username) != 0)
continue;
strtok(NULL, ":"); // skip password
strtok(NULL, ":"); // skip UID
char *gid_str = strtok(NULL, ":");
if (!gid_str)
break;
user_gid = atoi(gid_str);
break;
}
fclose(passwd);
if (user_gid < 0)
return 2;
// Step 2: Find group in /etc/group and check membership or GID match
FILE *group = fopen("/etc/group", "r");
if (!group)
return 2;
int found = 0;
while (fgets(line, sizeof(line), group)) {
char *grp = strtok(line, ":");
if (!grp || strcmp(grp, groupname) != 0)
continue;
strtok(NULL, ":"); // skip password
char *gid_str = strtok(NULL, ":");
if (!gid_str)
break;
int group_gid = atoi(gid_str);
if (group_gid == user_gid) {
puts("Yes");
fclose(group);
return 0;
}
char *members = strtok(NULL, ":\n");
if (!members)
break;
char *m = strtok(members, ",");
while (m) {
if (!strcmp(m, username)) {
puts("Yes");
fclose(group);
return 0;
}
m = strtok(NULL, ",");
}
found = 1;
break;
}
fclose(group);
if (!found)
return 2;
puts("No");
return 1;
}

59
chkgrp-native-min.c Normal file
View file

@ -0,0 +1,59 @@
/**
* chkgrp-min-native is a program that checks if a user is a member of a group.
*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* Homepage: https://git.xw3.org/hanez/chkusr
* License: Apache-2.0 (see LICENSE)
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
if (argc != 3)
return 2;
const char *username = argv[1];
const char *groupname = argv[2];
if (strlen(username) > MAX_NAME || strlen(groupname) > MAX_NAME)
return 2;
FILE *fp = fopen("/etc/group", "r");
if (!fp)
return 2;
char line[MAX_LINE];
while (fgets(line, sizeof(line), fp)) {
char *grp = strtok(line, ":");
if (!grp || strcmp(grp, groupname) != 0)
continue;
strtok(NULL, ":"); // skip password
strtok(NULL, ":"); // skip GID
char *members = strtok(NULL, ":\n");
if (!members) break;
char *m = strtok(members, ",");
while (m) {
if (!strcmp(m, username)) {
puts("Yes");
fclose(fp);
return 0;
}
m = strtok(NULL, ",");
}
break; // group matched, no user found
}
fclose(fp);
puts("No");
return 1;
}

77
chkgrp-native.c Normal file
View file

@ -0,0 +1,77 @@
/**
* chkgrp-native is a program that checks if a user is a member of a group.
*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* Homepage: https://git.xw3.org/hanez/chkusr
* License: Apache-2.0 (see LICENSE)
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr, "Usage: %s <username> <groupname>\n", argv[0]);
return 2;
}
const char *username = argv[1];
const char *groupname = argv[2];
if (strlen(username) > MAX_NAME || strlen(groupname) > MAX_NAME) {
fprintf(stderr, "Error: Name too long (max %d characters).\n", MAX_NAME);
return 2;
}
FILE *fp = fopen("/etc/group", "r");
if (!fp) {
perror("Error opening /etc/group");
return 2;
}
char line[MAX_LINE];
int found = 0;
while (fgets(line, sizeof(line), fp)) {
char *grp_name = strtok(line, ":");
if (!grp_name) continue;
strtok(NULL, ":"); // skip password
strtok(NULL, ":"); // skip GID
char *members = strtok(NULL, ":\n");
if (!members) continue;
if (strcmp(grp_name, groupname) != 0)
continue;
// Group found, now check for user in member list
found = 1;
char *member = strtok(members, ",");
while (member) {
if (strcmp(member, username) == 0) {
fclose(fp);
puts("Yes");
return 0;
}
member = strtok(NULL, ",");
}
break; // no need to continue scanning
}
fclose(fp);
if (!found) {
fprintf(stderr, "Group '%s' not found.\n", groupname);
return 2;
}
puts("No");
return 1;
}

152
chkgrp-ng.c Normal file
View file

@ -0,0 +1,152 @@
/**
* chkgrp-ng is a program that checks if a user is a member of a group.
*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* Homepage: https://git.xw3.org/hanez/chkusr
* License: Apache-2.0 (see LICENSE)
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void print_help(const char *prog)
{
printf("Usage: %s [OPTIONS] <username> <groupname>\n", prog);
printf("Options:\n");
printf(" --passwd PATH Use custom /etc/passwd file\n");
printf(" --group PATH Use custom /etc/group file\n");
printf(" -q Quiet mode (no output)\n");
printf(" -h Show this help message\n");
}
const char *get_arg(int *i, int argc, char *argv[], const char *opt)
{
if (*i + 1 >= argc) {
fprintf(stderr, "Missing argument after %s\n", opt);
exit(2);
}
return argv[++(*i)];
}
int get_user_gid(const char *user, const char *passwd_path)
{
FILE *fp = fopen(passwd_path, "r");
if (!fp) {
fprintf(stderr, "Error: Cannot open passwd file: %s\n", passwd_path);
return -1;
}
char line[MAX_LINE];
while (fgets(line, sizeof(line), fp)) {
char *name = strtok(line, ":");
if (!name || strcmp(name, user) != 0) continue;
strtok(NULL, ":"); // password
strtok(NULL, ":"); // UID
char *gid_str = strtok(NULL, ":");
fclose(fp);
return gid_str ? atoi(gid_str) : -1;
}
fclose(fp);
return -1;
}
int check_group(const char *user, const char *group, int user_gid,
const char *group_path)
{
FILE *fp = fopen(group_path, "r");
if (!fp) {
fprintf(stderr, "Error: Cannot open group file: %s\n", group_path);
return -1;
}
char line[MAX_LINE];
while (fgets(line, sizeof(line), fp)) {
char *grp = strtok(line, ":");
if (!grp || strcmp(grp, group) != 0) continue;
strtok(NULL, ":"); // password
char *gid_str = strtok(NULL, ":");
if (!gid_str) break;
int group_gid = atoi(gid_str);
if (group_gid == user_gid) {
fclose(fp);
return 1;
}
char *members = strtok(NULL, ":\n");
if (!members) break;
char *m = strtok(members, ",");
while (m) {
if (strcmp(m, user) == 0) {
fclose(fp);
return 1;
}
m = strtok(NULL, ",");
}
fclose(fp);
return 0;
}
fclose(fp);
return -1;
}
int main(int argc, char *argv[])
{
const char *passwd_file = "/etc/passwd";
const char *group_file = "/etc/group";
int quiet = 0;
int i = 1;
const char *username = NULL, *groupname = NULL;
while (i < argc) {
if (!strcmp(argv[i], "--passwd")) {
passwd_file = get_arg(&i, argc, argv, "--passwd");
} else if (!strcmp(argv[i], "--group")) {
group_file = get_arg(&i, argc, argv, "--group");
} else if (!strcmp(argv[i], "-q")) {
quiet = 1;
} else if (!strcmp(argv[i], "-h")) {
print_help(argv[0]);
return 0;
} else if (!username) {
username = argv[i];
} else if (!groupname) {
groupname = argv[i];
} else {
fprintf(stderr, "Unexpected argument: %s\n", argv[i]);
return 2;
}
i++;
}
if (!username || !groupname ||
strlen(username) > MAX_NAME || strlen(groupname) > MAX_NAME) {
return 2;
}
int user_gid = get_user_gid(username, passwd_file);
if (user_gid < 0)
return 2;
int result = check_group(username, groupname, user_gid, group_file);
if (result == 1) {
if (!quiet) puts("Yes");
return 0;
} else if (result == 0) {
if (!quiet) puts("No");
return 1;
}
return 2;
}

75
chkgrp.c Normal file
View file

@ -0,0 +1,75 @@
/**
* chkgrp is a program that checks if a user is a member of a group.
*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* Homepage: https://git.xw3.org/hanez/chkusr
* License: Apache-2.0 (see LICENSE)
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <errno.h>
#include <limits.h>
int main(int argc, char *argv[])
{
if (argc != 3) {
fprintf(stderr, "Usage: %s <username> <groupname>\n", argv[0]);
return 2;
}
const char *username = argv[1];
const char *groupname = argv[2];
// Runtime check for max name length
long max_name_len = sysconf(_SC_LOGIN_NAME_MAX);
if (max_name_len <= 0 || max_name_len > 1024) {
max_name_len = MAX_NAME;
}
if (strlen(username) > (size_t)max_name_len) {
fprintf(stderr, "Error: Username too long (max %ld characters).\n",
max_name_len);
return 2;
}
if (strlen(groupname) > (size_t)max_name_len) {
fprintf(stderr, "Error: Group name too long (max %ld characters).\n",
max_name_len);
return 2;
}
struct passwd *pw = getpwnam(username);
if (!pw) {
fprintf(stderr, "Error: User '%s' not found.\n", username);
return 2;
}
struct group *gr = getgrnam(groupname);
if (!gr) {
fprintf(stderr, "Error: Group '%s' not found.\n", groupname);
return 2;
}
if (pw->pw_gid == gr->gr_gid) {
puts("Yes");
return 0;
}
for (char **members = gr->gr_mem; *members != NULL; members++) {
if (strcmp(*members, username) == 0) {
puts("Yes");
return 0;
}
}
puts("No");
return 1;
}

27
chkpwd.1 Normal file
View file

@ -0,0 +1,27 @@
.TH checkpw 1 "June 2025" "chkpwd 1.4.0" "chkpwd man page"
.SH NAME
chkpwd \- checks the validity of a users password on a UNIX/PAM-based system.
.SH SYNOPSIS
chkpwd [OPTION]...
.SH DESCRIPTION
chkpwd is a program that checks the validity of a users password on a
UNIX/PAM-based system.
.SH OPTIONS
The options which apply to the chkpwd command are:
-u <username> Set username.
-p <password> Set password.
-v Enable verbose mode.
-V Print program version.
-h Show this help.
chkpwd runs in an interactive mode when no username and/or password are set.
A missing username and/or password will then be asked for while program
execution.
.SH SEE ALSO
pam(3), pam_authenticate(3), PAM(8), pam_unix(8)
.SH BUGS
No known bugs.
.SH AUTHOR
Written by Johannes Findeisen <you@hanez.org>.

170
chkpwd.c Normal file
View file

@ -0,0 +1,170 @@
/**
* chkpwd is a program that checks the validity of a users password on a
* UNIX/PAM-based system.
*
* Author: Johannes Findeisen <you@hanez.org> - 2024
* Homepage: https://git.xw3.org/hanez/chkpwd
* License: Apache-2.0 (see LICENSE)
*/
#include "chkpwd.h"
#include "config.h"
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <termios.h>
#include <unistd.h>
// Function to prompt user for input, optionally hiding input
void prompt_for_input(char *buffer, size_t size, const char *prompt,
bool hide_input)
{
printf("%s", prompt);
fflush(stdout);
if (hide_input) {
struct termios oldt, newt;
if (tcgetattr(STDIN_FILENO, &oldt) != 0) {
perror("tcgetattr");
exit(1);
}
newt = oldt;
newt.c_lflag &= ~(ECHO);
if (tcsetattr(STDIN_FILENO, TCSANOW, &newt) != 0) {
perror("tcsetattr");
exit(1);
}
}
if (fgets(buffer, size, stdin) == NULL) {
fprintf(stderr, "\nError reading input.\n");
if (hide_input) {
// Restore terminal settings
struct termios oldt;
tcgetattr(STDIN_FILENO, &oldt);
oldt.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
}
exit(1);
}
// Remove trailing newline
buffer[strcspn(buffer, "\n")] = '\0';
if (hide_input) {
// Restore terminal settings
struct termios oldt;
tcgetattr(STDIN_FILENO, &oldt);
oldt.c_lflag |= ECHO;
tcsetattr(STDIN_FILENO, TCSANOW, &oldt);
printf("\n"); // Move to the next line after password input
}
}
void print_usage(const char *prog_name)
{
printf("\n");
printf("Usage: %s [-u <username>] [-p <password>] [-v] [-V] [-h]\n",
prog_name);
printf("\n");
printf("Options:\n");
printf(" -u <username> Set username (if not set, the program asks for it).\n");
printf(" -p <password> Set password (if not set, the program asks for it).\n");
printf(" -v Enable verbose mode.\n");
printf(" -V Print program version.\n");
printf(" -h Show this help.\n");
printf("\n");
}
int main(int argc, char *argv[])
{
bool verbose = false;
bool version = false;
char password[MAX_PASSWORD] = {0};
char username[MAX_NAME] = {0};
int opt;
// Parse command-line arguments
while ((opt = getopt(argc, argv, "u:p:hvV")) != -1) {
switch (opt) {
case 'u':
if (strlen(optarg) >= MAX_NAME) {
fprintf(stderr, "Error: Username is too long (maximum %d characters).\n",
MAX_NAME);
exit(1);
}
strncpy(username, optarg, MAX_NAME - 1);
break;
case 'p':
if (strlen(optarg) >= MAX_PASSWORD) {
fprintf(stderr, "Error: Password is too long (maximum %d characters).\n",
MAX_PASSWORD);
exit(1);
}
strncpy(password, optarg, MAX_PASSWORD - 1);
break;
case 'h':
print_usage(argv[0]);
exit(0);
case 'v':
verbose = true;
break;
case 'V':
version = true;
break;
default:
break;
}
}
if (version) {
printf("%s\n", VERSION);
exit(0);
}
if (username[0] == '\0') {
prompt_for_input(username, sizeof(username), "Username: ", false);
if (strlen(username) == 0) {
fprintf(stderr, "Error: Username cannot be empty.\n");
exit(1);
}
}
if (password[0] == '\0') {
prompt_for_input(password, sizeof(password), "Password: ", true);
if (strlen(password) == 0) {
fprintf(stderr, "Error: Password cannot be empty.\n");
exit(1);
}
}
// Retrieve user information from the username
struct passwd *pwd = getpwnam(username);
if (pwd == NULL) {
fprintf(stderr, "Error: User '%s' not found.\n", username);
exit(1);
}
// Check if the user's UID is below the minimum allowed UID and not higher
// than maximum allowed UID
if (pwd->pw_uid < MIN_UID || pwd->pw_uid > MAX_UID) {
fprintf(stderr, "Error: User '%s' has a UID higher than %d or lower than %d and is not allowed to authenticate.\n",
username, MAX_UID, MIN_UID);
exit(1);
}
if (verbose)
printf("User '%s' passed UID check (UID: %d).\n", username,
pwd->pw_uid);
if (checkpw_authenticate(username, password, verbose) == true) {
printf("Authenticated successfully.\n");
return 0;
} else {
printf("Authentication failed.\n");
return 1;
}
}

107
chkpwd.h Normal file
View file

@ -0,0 +1,107 @@
/**
* chkpwd.h is part of chkusr, a set of programs to verify information about
* a user on UNIX based systems.
*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* Homepage: https://git.xw3.org/hanez/chkusr
* License: Apache-2.0 (see LICENSE)
*/
#include <pwd.h>
#include <stdbool.h>
#include <security/pam_appl.h>
#include <security/pam_misc.h>
bool checkpw_authenticate(const char *username, const char *password,
bool verbose);
int pam_conversation(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr);
struct pam_credentials
{
const char *password;
};
bool checkpw_authenticate(const char *username, const char *password,
bool verbose)
{
int retval;
pam_handle_t *pamh = NULL;
struct pam_credentials credentials = { password };
struct pam_conv conv = { pam_conversation, &credentials };
if (verbose)
printf("Starting PAM authentication for user '%s'.\n", username);
retval = pam_start("login", username, &conv, &pamh);
if (retval == PAM_SUCCESS) {
if (verbose)
printf("PAM authentication initialized.\n");
retval = pam_authenticate(pamh, 0); // Attempt to authenticate
} else {
if (verbose)
fprintf(stderr, "Error: pam_start failed: %s\n", pam_strerror(pamh,
retval));
}
if (retval == PAM_SUCCESS) {
if (verbose)
printf("User '%s' authenticated successfully.\n", username);
retval = pam_acct_mgmt(pamh, 0); // Check account validity
if (retval != PAM_SUCCESS && verbose)
fprintf(stderr, "Error: pam_acct_mgmt failed: %s\n",
pam_strerror(pamh, retval));
} else {
if (verbose)
fprintf(stderr, "Error: pam_authenticate failed: %s\n",
pam_strerror(pamh, retval));
}
if (pam_end(pamh, retval) != PAM_SUCCESS) {
pamh = NULL;
fprintf(stderr, "Error: Failed to release PAM authenticator.\n");
exit(1);
}
if (retval != PAM_SUCCESS && verbose) {
fprintf(stderr, "Error: Authentication failed for user '%s'.\n",
username);
}
return (retval == PAM_SUCCESS ? true : false);
}
// PAM conversation function to supply the password
int pam_conversation(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr)
{
struct pam_response *response = NULL;
struct pam_credentials *credentials = (struct pam_credentials *)appdata_ptr;
int i;
response = (struct pam_response *)malloc(sizeof(struct pam_response)
* num_msg);
if (response == NULL)
return PAM_CONV_ERR;
for (i = 0; i < num_msg; i++) {
switch (msg[i]->msg_style) {
case PAM_PROMPT_ECHO_ON:
case PAM_PROMPT_ECHO_OFF:
response[i].resp = strdup(credentials->password);
response[i].resp_retcode = 0;
break;
default:
free(response);
return PAM_CONV_ERR;
}
}
*resp = response;
return PAM_SUCCESS;
}

100
chkpwr.c Normal file
View file

@ -0,0 +1,100 @@
/**
* checkpwrule is a program that checks if an input string validates a
* password rule.
*
* Author: Johannes Findeisen <you@hanez.org> - 2024
* License: MIT (see LICENSE)
*/
#include "config.h"
#include <stdio.h>
#include <ctype.h>
#include <string.h>
// Return 1 if non-ASCII characters are found, 0 if all are ASCII
int is_valid_ascii(char *str) {
for (int i = 0; i < strlen(str); i++) {
if ((unsigned char)str[i] > 127) {
return 1; // Non-ASCII character detected, return 1 (indicating invalid)
}
}
return 0; // All characters are ASCII
}
int is_valid(char *str) {
int lowercase_count = 0, uppercase_count = 0, digit_count = 0, special_count = 0;
int i;
int length = strlen(str);
// Check if the input contains any non-ASCII characters
if (is_valid_ascii(str)) {
printf("Password contains non-ASCII characters.\n");
return 1; // Invalid input, return 1 to signal failure
}
// Check if the length is within the allowed range
if (length < MIN_PASSWORD_LENGTH) {
printf("Password is too short! It must be at least %d characters long.\n", MIN_PASSWORD_LENGTH);
return 1;
}
if (length > MAX_PASSWORD_LENGTH) {
printf("Password exceeds the maximum length of %d characters.\n", MAX_PASSWORD_LENGTH);
return 1;
}
// Check each character of the string and count occurrences
for (i = 0; i < length; i++) {
if (islower(str[i])) {
lowercase_count++;
} else if (isupper(str[i])) {
uppercase_count++;
} else if (isdigit(str[i])) {
digit_count++;
} else if (ispunct(str[i])) { // Checks for special characters like !, @, #, etc.
special_count++;
}
}
// Check if all counts meet the required minimums
if (lowercase_count < MIN_LOWERCASE) {
printf("Password needs at least %d lowercase letters.\n", MIN_LOWERCASE);
return 1;
}
if (uppercase_count < MIN_UPPERCASE) {
printf("Password needs at least %d uppercase letters.\n", MIN_UPPERCASE);
return 1;
}
if (digit_count < MIN_DIGITS) {
printf("Password needs at least %d digits.\n", MIN_DIGITS);
return 1;
}
if (special_count < MIN_SPECIAL) {
printf("Password needs at least %d special characters.\n", MIN_SPECIAL);
return 1;
}
// If all conditions are met, return 0 (indicating success)
return 0;
}
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("Usage: %s <string>\n", argv[0]);
return 1;
}
if (is_valid(argv[1]) == 0) {
printf("The string is valid.\n");
return 0; // Success
} else {
printf("The string is invalid. It must contain at least:\n");
printf("- %d lowercase letters\n", MIN_LOWERCASE);
printf("- %d uppercase letters\n", MIN_UPPERCASE);
printf("- %d digits\n", MIN_DIGITS);
printf("- %d special characters\n", MIN_SPECIAL);
printf("And be between %d and %d characters long.\n", MIN_PASSWORD_LENGTH, MAX_PASSWORD_LENGTH);
return 1; // Invalid password, return 1
}
}

56
config.h Normal file
View file

@ -0,0 +1,56 @@
/*
* Author: Johannes Findeisen <you@hanez.org> - 2025
* Homepage: https://git.xw3.org/hanez/chkusr
* License: Apache-2.0 (see LICENSE)
*/
#define VERSION "1.5.1"
/* For chkgrp */
#ifndef MAX_LINE
#define MAX_LINE 1024
#endif
/* MAX_NAME is also used by chkpwd */
#ifndef MAX_NAME
#define MAX_NAME 256
#endif
/* For chkpwd; Also uses MAX_NAME from chkgrp above */
#ifndef MAX_PASSWORD
#define MAX_PASSWORD 256
#endif
#ifndef MAX_UID
#define MAX_UID 1000
#endif
#ifndef MIN_UID
#define MIN_UID 1000
#endif
/* For chkpwr */
#ifndef MAX_PASSWORD_LENGTH
#define MAX_PASSWORD_LENGTH 32
#endif
#ifndef MIN_PASSWORD_LENGTH
#define MIN_PASSWORD_LENGTH 12
#endif
#ifndef MIN_LOWERCASE
#define MIN_LOWERCASE 2
#endif
#ifndef MIN_UPPERCASE
#define MIN_UPPERCASE 2
#endif
#ifndef MIN_DIGITS
#define MIN_DIGITS 2
#endif
#ifndef MIN_SPECIAL
#define MIN_SPECIAL 2
#endif

1
lua/README.md Normal file
View file

@ -0,0 +1 @@
This is experimental at the monent but I really will implement some programs from chkusr in Lua or write some wrappers for the C programs...

79
lua/checkgroup.lua Executable file
View file

@ -0,0 +1,79 @@
-- This file is part of chkgrp that is a program that checks if a user is a
-- member of a group.
--
-- Author: Johannes Findeisen <you@hanez.org> - 2025
-- Homepage: https://git.xw3.org/hanez/chkgrp
-- License: Apache-2.0 (see LICENSE)
local checkgroup = {}
local MAX_NAME = 256
local function read_passwd_gid(username)
local f = io.open("/etc/passwd", "r")
if not f then return nil end
for line in f:lines() do
local fields = {}
for field in string.gmatch(line, "([^:]+)") do
table.insert(fields, field)
end
if fields[1] == username then
f:close()
return tonumber(fields[4])
end
end
f:close()
return nil
end
local function check_group_membership(username, groupname, user_gid)
local f = io.open("/etc/group", "r")
if not f then return nil end
for line in f:lines() do
local fields = {}
for field in string.gmatch(line, "([^:]+)") do
table.insert(fields, field)
end
if fields[1] == groupname then
local group_gid = tonumber(fields[3])
if group_gid == user_gid then
f:close()
return true
end
for member in string.gmatch(fields[4] or "", "[^,]+") do
if member == username then
f:close()
return true
end
end
f:close()
return false
end
end
f:close()
return nil
end
function checkgroup.is_user_in_group(username, groupname)
if not username or not groupname then
return nil
end
if #username > MAX_NAME or #groupname > MAX_NAME then
return nil
end
local gid = read_passwd_gid(username)
if not gid then return nil end
return check_group_membership(username, groupname, gid)
end
return checkgroup

View file

@ -0,0 +1,89 @@
#!/usr/bin/env lua
-- This file is part of chkgrp that is a program that checks if a user is a
-- member of a group.
--
-- Author: Johannes Findeisen <you@hanez.org> - 2025
-- Homepage: https://git.xw3.org/hanez/chkgrp
-- License: Apache-2.0 (see LICENSE)
local MAX_NAME = 256
local function read_passwd_gid(username)
local f = io.open("/etc/passwd", "r")
if not f then return nil end
for line in f:lines() do
local fields = {}
for field in string.gmatch(line, "([^:]+)") do
table.insert(fields, field)
end
if fields[1] == username then
f:close()
return tonumber(fields[4]) -- GID is 4th field
end
end
f:close()
return nil
end
local function check_group_membership(username, groupname, user_gid)
local f = io.open("/etc/group", "r")
if not f then return nil end
for line in f:lines() do
local fields = {}
for field in string.gmatch(line, "([^:]+)") do
table.insert(fields, field)
end
if fields[1] == groupname then
local group_gid = tonumber(fields[3])
if group_gid == user_gid then
f:close()
return true
end
for member in string.gmatch(fields[4] or "", "[^,]+") do
if member == username then
f:close()
return true
end
end
f:close()
return false
end
end
f:close()
return nil
end
local username = arg[1]
local groupname = arg[2]
if not username or not groupname then
os.exit(2)
end
if #username > MAX_NAME or #groupname > MAX_NAME then
os.exit(2)
end
local user_gid = read_passwd_gid(username)
if not user_gid then
os.exit(2)
end
local result = check_group_membership(username, groupname, user_gid)
if result == nil then
os.exit(2)
elseif result == true then
print("Yes")
os.exit(0)
else
print("No")
os.exit(1)
end

26
lua/test.lua Executable file
View file

@ -0,0 +1,26 @@
#!/usr/bin/env lua
-- This file is part of chkgrp that is a program that checks if a user is a
-- member of a group.
--
-- Author: Johannes Findeisen <you@hanez.org> - 2025
-- Homepage: https://git.xw3.org/hanez/chkgrp
-- License: Apache-2.0 (see LICENSE)
local checkgroup = require("checkgroup")
local user = arg[1]
local group = arg[2]
local result = checkgroup.is_user_in_group(user, group)
if result == true then
print("Yes")
os.exit(0)
elseif result == false then
print("No")
os.exit(1)
else
os.exit(2)
end