Compare commits
74 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| cb8edf2913 | |||
| fd829f1569 | |||
| 37a10d5c07 | |||
| 5a092b3b65 | |||
| b082f1592b | |||
| 7adcaf989f | |||
| 025f792261 | |||
| 9709333e76 | |||
| 22303a3506 | |||
| f9e1cf4c05 | |||
| 92fdb05161 | |||
| 71555fc275 | |||
| 0957057793 | |||
| eb69c6989c | |||
| d92c2d2a76 | |||
| f8870f6c80 | |||
| 45973a3a90 | |||
| 12ea8e8273 | |||
| bf732541c0 | |||
| 8644ccbdc5 | |||
| b2461ba7a0 | |||
| dbd240f31f | |||
| ccfd170bae | |||
| ce7fc820a7 | |||
| 3d0022ced1 | |||
| d819e7c821 | |||
| a2bcc55b38 | |||
| 0fb2bc12e7 | |||
| bdc6fab467 | |||
| 22e4f0790a | |||
| 1665cdc1a3 | |||
| bec3be6746 | |||
| 4c50974efc | |||
| 6d3e3393b5 | |||
| 687777f90b | |||
| bc4713e82e | |||
| 0b7219c6d8 | |||
| 5e80936967 | |||
| bd2ac5b829 | |||
| f53aaced69 | |||
| ae586d053b | |||
| 66d7b352d6 | |||
| 84d481aa6f | |||
| ec9b6c38ae | |||
| e0a32fcedc | |||
| 27bfe141d1 | |||
| 23d4719078 | |||
| 4e0bd0df75 | |||
| 6ee75766b0 | |||
| d7780a2d41 | |||
| 82aced57b6 | |||
| 6f859b18b2 | |||
| 70550ffa7f | |||
| 5bd3572c75 | |||
| 97d9abd6c1 | |||
|
|
a771e28f3b | ||
| 7b0d7ce7a7 | |||
| 2cc8792452 | |||
| 04ad31fb2e | |||
| 60919c1ca8 | |||
| 7ffe30647b | |||
| 1ee8e07b0a | |||
| e7b7397c93 | |||
| 4efe3efa96 | |||
| 689d1cbe18 | |||
| f0b2352975 | |||
| a1f63c428c | |||
| 1311e992f8 | |||
| 5115ad76d9 | |||
| d5a27484d0 | |||
| 03afded918 | |||
| 98249359c5 | |||
| c4caadde93 | |||
| 5a992efb7e |
20 changed files with 1352 additions and 304 deletions
15
.gitignore
vendored
15
.gitignore
vendored
|
|
@ -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
16
LICENSE
|
|
@ -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.
|
||||
|
||||
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.
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
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.
|
||||
|
|
|
|||
48
Makefile
48
Makefile
|
|
@ -1,12 +1,52 @@
|
|||
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:
|
||||
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:
|
||||
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:
|
||||
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
150
README.md
|
|
@ -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
|
||||
cd checkpw
|
||||
git clone https://git.xw3.org/hanez/chkusr.git
|
||||
cd chkusr
|
||||
make
|
||||
```
|
||||
|
||||
#### Installation
|
||||
|
||||
**WARNING:** Install this software with care. chkpwd could easily be used for bruteforcing passwords from local users!
|
||||
|
||||
```
|
||||
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 cp ./checkpw /usr/bin/
|
||||
sudo make uninstall
|
||||
```
|
||||
|
||||
## 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
|
||||
Usage: checkpw [-u <username>] [-p <password>] [-i] [-v] [-h]
|
||||
chkpwd -h
|
||||
Usage: chkpwd [-u <username>] [-p <password>] [-v] [-V] [-h]
|
||||
|
||||
Options:
|
||||
-u <username> Specify username.
|
||||
-p <password> Specify password.
|
||||
-i Enable interactive mode to prompt for missing username/password.
|
||||
-u <username> Set username.
|
||||
-p <password> Set password.
|
||||
-v Enable verbose mode.
|
||||
-V Print program version.
|
||||
-h Show this help.
|
||||
```
|
||||
|
||||
Returns 0 on success, 1 otherwise.
|
||||
|
||||
### Examples:
|
||||
|
||||
#### Interactive mode:
|
||||
You can also use chkpwd even without installing by just running the following command:
|
||||
|
||||
```
|
||||
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 $?
|
||||
```
|
||||
|
||||
##### 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
264
checkpw.c
|
|
@ -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
48
chkgrp-min.c
Normal 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;
|
||||
}
|
||||
|
||||
101
chkgrp-native-min-extended.c
Normal file
101
chkgrp-native-min-extended.c
Normal 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
59
chkgrp-native-min.c
Normal 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
77
chkgrp-native.c
Normal 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
152
chkgrp-ng.c
Normal 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
75
chkgrp.c
Normal 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
27
chkpwd.1
Normal 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
170
chkpwd.c
Normal 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
107
chkpwd.h
Normal 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
100
chkpwr.c
Normal 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
56
config.h
Normal 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
1
lua/README.md
Normal 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
79
lua/checkgroup.lua
Executable 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
|
||||
|
||||
89
lua/chkgrp-native-min-extended.lua
Executable file
89
lua/chkgrp-native-min-extended.lua
Executable 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
26
lua/test.lua
Executable 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
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue