diff --git a/.gitignore b/.gitignore index 5188112..d980a9c 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,13 @@ -chkpw -chkpw.o -chkpw.1.gz +*.o +*.so +chkgrp +chkgrp-min +chkgrp-native +chkgrp-native-min +chkgrp-native-min-extended +chkgrp-ng +chkpwd +chkpwd.1.gz +chkpwr test test.c diff --git a/Makefile b/Makefile index 2498611..1628df2 100644 --- a/Makefile +++ b/Makefile @@ -1,19 +1,49 @@ all: + $(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 ./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 -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/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 diff --git a/README.md b/README.md index f59d457..d7e1945 100644 --- a/README.md +++ b/README.md @@ -1,36 +1,51 @@ -# chkpwd +# chkusr -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. +chkusr is a set of programs (chkgrp, chkpwd and chkpwr) to verify information about a user and/or group on UNIX based systems. -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 FOLLOWING DOCUMENTATION WILL NOT WORK ACTUALLY! YOU NEED TO READ THE CODE! SORRY... -## The idea + * [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) -I needed a program to verify passwords of users on Linux/UNIX systems using PAM that just returns 0 on success and 1 on error. +### The idea -## Requirements +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. -You need the PAM development package installed. On Alpine it is named linux-pam-dev, on Debian based systems it is libpam0g-dev. +#### Requirements -## Building chkpwd +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/chkpwd.git -cd chkpwd +git clone https://git.xw3.org/hanez/chkusr.git +cd chkusr make ``` -The code only supports verifying passwords for user id 1000 by default. Look at the file chkpwd.h for some compile time options! - -### Custom build example - -Set MAX_UID and MIN_UID at compile time: - -``` -gcc -Wall -DMAX_UID=1000 -DMIN_UID=1000 -o chkpwd chkpwd.c -lpam -lpam_misc -``` - -## Installation +#### Installation **WARNING:** Install this software with care. chkpwd could easily be used for bruteforcing passwords from local users! @@ -38,17 +53,33 @@ gcc -Wall -DMAX_UID=1000 -DMIN_UID=1000 -o chkpwd chkpwd.c -lpam -lpam_misc sudo make install ``` -chkpwd is installed to /usr/bin/. +chkusr programs are installed to /usr/bin/. chkpwd.h is installed to /usr/include/ for use in other applications. -## Uninstall +#### Uninstall ``` 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 ``` chkpwd -h @@ -68,47 +99,64 @@ You can also use chkpwd even without installing by just running the following co ./chkpwd ``` -### Return codes +##### Return codes -chkpwd returns 0 on success, 1 otherwise. +chk returns 0 on success, 1 otherwise. -### Examples +##### Examples - -#### Interactive mode asking for a username and a password +###### Interactive mode asking for a username and a password ``` chkpwd ``` -#### Interactive mode only asking for a password +###### Interactive mode only asking for a password ``` chkpwd -u hanez ``` -#### None interactive mode with username and password provided as arguments to chkpwd +###### None interactive mode with username and password provided as arguments to chkpwd ``` chkpwd -u hanez -p password ``` -#### Request the result from the above commands +###### Request the result from the above commands ``` echo $? ``` -## License +##### Custom build -chkpwd is licensed under the Apache License, Version 2.0. +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. -## Links - - - [https://git.xw3.org/hanez/chkpwd](https://git.xw3.org/hanez/chkpwd) - - [https://linux.die.net/man/8/unix_chkpwdd](https://linux.die.net/man/8/unix_chkpwd) - - [https://cr.yp.to/checkpwd.html](https://cr.yp.to/checkpwd.html) - - [https://pamtester.sourceforge.net/](https://pamtester.sourceforge.net/) diff --git a/chkgrp-min.c b/chkgrp-min.c new file mode 100644 index 0000000..4bc4dcb --- /dev/null +++ b/chkgrp-min.c @@ -0,0 +1,48 @@ +/** + * chkgrp-min is a program that checks if a user is a member of a group. + * + * Author: Johannes Findeisen - 2025 + * Homepage: https://git.xw3.org/hanez/chkusr + * License: Apache-2.0 (see LICENSE) + */ + + #include "config.h" + +#include +#include +#include +#include +#include + +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; +} + diff --git a/chkgrp-native-min-extended.c b/chkgrp-native-min-extended.c new file mode 100644 index 0000000..320c57f --- /dev/null +++ b/chkgrp-native-min-extended.c @@ -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 - 2025 + * Homepage: https://git.xw3.org/hanez/chkusr + * License: Apache-2.0 (see LICENSE) + */ + +#include "config.h" + +#include +#include +#include +#include + +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; +} + diff --git a/chkgrp-native-min.c b/chkgrp-native-min.c new file mode 100644 index 0000000..8786a20 --- /dev/null +++ b/chkgrp-native-min.c @@ -0,0 +1,59 @@ +/** + * chkgrp-min-native is a program that checks if a user is a member of a group. + * + * Author: Johannes Findeisen - 2025 + * Homepage: https://git.xw3.org/hanez/chkusr + * License: Apache-2.0 (see LICENSE) + */ + +#include "config.h" + +#include +#include +#include + +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; +} + diff --git a/chkgrp-native.c b/chkgrp-native.c new file mode 100644 index 0000000..d19de61 --- /dev/null +++ b/chkgrp-native.c @@ -0,0 +1,77 @@ +/** + * chkgrp-native is a program that checks if a user is a member of a group. + * + * Author: Johannes Findeisen - 2025 + * Homepage: https://git.xw3.org/hanez/chkusr + * License: Apache-2.0 (see LICENSE) + */ + +#include "config.h" + +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s \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; +} + diff --git a/chkgrp-ng.c b/chkgrp-ng.c new file mode 100644 index 0000000..e22c053 --- /dev/null +++ b/chkgrp-ng.c @@ -0,0 +1,152 @@ +/** + * chkgrp-ng is a program that checks if a user is a member of a group. + * + * Author: Johannes Findeisen - 2025 + * Homepage: https://git.xw3.org/hanez/chkusr + * License: Apache-2.0 (see LICENSE) + */ + +#include "config.h" + +#include +#include +#include + +void print_help(const char *prog) +{ + printf("Usage: %s [OPTIONS] \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; +} + diff --git a/chkgrp.c b/chkgrp.c new file mode 100644 index 0000000..35fcd75 --- /dev/null +++ b/chkgrp.c @@ -0,0 +1,75 @@ +/** + * chkgrp is a program that checks if a user is a member of a group. + * + * Author: Johannes Findeisen - 2025 + * Homepage: https://git.xw3.org/hanez/chkusr + * License: Apache-2.0 (see LICENSE) + */ + +#include "config.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +int main(int argc, char *argv[]) +{ + if (argc != 3) { + fprintf(stderr, "Usage: %s \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; +} + diff --git a/chkpwd.1 b/chkpwd.1 index cc83125..da6b5cb 100644 --- a/chkpwd.1 +++ b/chkpwd.1 @@ -1,10 +1,11 @@ -.TH man 1 "01 Jun 2025" "chkpwd 1.3.0" "chkpwd man page" +.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. +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: @@ -14,11 +15,13 @@ The options which apply to the chkpwd command are: -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 program execution. +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(3), pam_authenticate(3), PAM(8), pam_unix(8) .SH BUGS No known bugs. .SH AUTHOR -Johannes Findeisen (you@hanez.org) +Written by Johannes Findeisen . diff --git a/chkpwd.c b/chkpwd.c index 7339620..4273372 100644 --- a/chkpwd.c +++ b/chkpwd.c @@ -3,10 +3,12 @@ * UNIX/PAM-based system. * * Author: Johannes Findeisen - 2024 + * Homepage: https://git.xw3.org/hanez/chkpwd * License: Apache-2.0 (see LICENSE) */ #include "chkpwd.h" +#include "config.h" #include #include @@ -15,22 +17,6 @@ #include #include -#ifndef MAX_PASSWORD_LEN -#define MAX_PASSWORD_LEN 256 -#endif - -#ifndef MAX_USERNAME_LEN -#define MAX_USERNAME_LEN 32 -#endif - -#ifndef MAX_UID -#define MAX_UID 1000 -#endif - -#ifndef MIN_UID -#define MIN_UID 1000 -#endif - // Function to prompt user for input, optionally hiding input void prompt_for_input(char *buffer, size_t size, const char *prompt, bool hide_input) @@ -96,28 +82,28 @@ int main(int argc, char *argv[]) { bool verbose = false; bool version = false; - char password[MAX_PASSWORD_LEN] = {0}; - char username[MAX_USERNAME_LEN] = {0}; + 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_USERNAME_LEN) { + if (strlen(optarg) >= MAX_NAME) { fprintf(stderr, "Error: Username is too long (maximum %d characters).\n", - MAX_USERNAME_LEN); + MAX_NAME); exit(1); } - strncpy(username, optarg, MAX_USERNAME_LEN - 1); + strncpy(username, optarg, MAX_NAME - 1); break; case 'p': - if (strlen(optarg) >= MAX_PASSWORD_LEN) { + if (strlen(optarg) >= MAX_PASSWORD) { fprintf(stderr, "Error: Password is too long (maximum %d characters).\n", - MAX_PASSWORD_LEN); + MAX_PASSWORD); exit(1); } - strncpy(password, optarg, MAX_PASSWORD_LEN - 1); + strncpy(password, optarg, MAX_PASSWORD - 1); break; case 'h': print_usage(argv[0]); diff --git a/chkpwd.h b/chkpwd.h index ef1e97b..d97c229 100644 --- a/chkpwd.h +++ b/chkpwd.h @@ -1,17 +1,16 @@ /** - * chkpwd.h is part of chkpwd, a program that checks the validity of a users - * password on a UNIX/PAM-based system. + * chkpwd.h is part of chkusr, a set of programs to verify information about + * a user on UNIX based systems. * * Author: Johannes Findeisen - 2025 + * Homepage: https://git.xw3.org/hanez/chkusr * License: Apache-2.0 (see LICENSE) */ #include +#include #include #include -#include - -#define VERSION "1.3.0" bool checkpw_authenticate(const char *username, const char *password, bool verbose); diff --git a/chkpwr.c b/chkpwr.c new file mode 100644 index 0000000..e6be5eb --- /dev/null +++ b/chkpwr.c @@ -0,0 +1,100 @@ +/** + * checkpwrule is a program that checks if an input string validates a + * password rule. + * + * Author: Johannes Findeisen - 2024 + * License: MIT (see LICENSE) + */ + +#include "config.h" + +#include +#include +#include + +// 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 \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 + } +} + diff --git a/config.h b/config.h new file mode 100644 index 0000000..be0bc9a --- /dev/null +++ b/config.h @@ -0,0 +1,56 @@ +/* + * Author: Johannes Findeisen - 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 + diff --git a/lua/README.md b/lua/README.md new file mode 100644 index 0000000..f891075 --- /dev/null +++ b/lua/README.md @@ -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... diff --git a/lua/checkgroup.lua b/lua/checkgroup.lua new file mode 100755 index 0000000..a8ee0db --- /dev/null +++ b/lua/checkgroup.lua @@ -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 - 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 + diff --git a/lua/chkgrp-native-min-extended.lua b/lua/chkgrp-native-min-extended.lua new file mode 100755 index 0000000..6b5f22f --- /dev/null +++ b/lua/chkgrp-native-min-extended.lua @@ -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 - 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 + diff --git a/lua/test.lua b/lua/test.lua new file mode 100755 index 0000000..0bfb5e8 --- /dev/null +++ b/lua/test.lua @@ -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 - 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 +