/* * UNG's Not GNU * * Copyright (c) 2020 Jakob Kaivo * * 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: * * 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. */ #define _POSIX_C_SOURCE 200809L #include #include #include #include #include #include #include #include #include #include #ifndef LINE_MAX #define LINE_MAX _POSIX2_LINE_MAX #endif #ifndef O_SEARCH #define O_SEARCH 0 #endif #define USERS_DIRECTORY "/var/secure/users" static DIR *users_dir = NULL; struct user { struct passwd pw; char gecos[LINE_MAX]; char password[LINE_MAX]; }; enum selection { UID, NAME, ALL }; static struct user *user_read(char *name) { static struct user u = { 0 }; int user = openat(dirfd(users_dir), name, O_SEARCH); if (user == -1) { return NULL; } struct stat st; if (fstat(user, &st) != 0) { close(user); return NULL; } u.pw.pw_name = name; u.pw.pw_uid = st.st_uid; u.pw.pw_gid = st.st_gid; u.pw.pw_dir = "/"; // TODO: read symlink at userfd, home u.pw.pw_shell = "/bin/sh"; // TOOD: read symlink at userfd, shell strcpy(u.gecos, name); // TODO: read file at userfd, gecos strcpy(u.password, "--"); // TODO: read file at userfd, password close(user); return &u; } static char *user_uid(uid_t uid) { char *name = NULL; struct dirent *de; while ((de = readdir(users_dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { continue; } struct stat st; if (fstatat(dirfd(users_dir), de->d_name, &st, 0) != 0) { continue; } if (st.st_uid == uid) { name = de->d_name; break; } } return name; } static struct user *user_find(enum selection sel, char *name, uid_t uid) { if (sel == UID) { name = user_uid(uid); } return user_read(name); } static void user_print(const struct user *u) { if (u == NULL) { return; } printf("%s:%s:%jd:%jd:%s:%s:%s\n", u->pw.pw_name, u->password, (intmax_t)u->pw.pw_uid, (intmax_t)u->pw.pw_gid, u->gecos, u->pw.pw_dir, u->pw.pw_shell); } int main(int argc, char *argv[]) { char *name = getlogin(); uid_t uid = getuid(); enum selection sel = NAME;; enum { LIST, PASSWORD } action = LIST; int c; while ((c = getopt(argc, argv, "LN:U:p")) != -1) { switch (c) { case 'L': sel = ALL; break; case 'N': sel = NAME; name = optarg; break; case 'U': sel = UID; uid = atoi(optarg); break; case 'p': action = PASSWORD; break; default: return 1; } } if (action == PASSWORD && sel == ALL) { fprintf(stderr, "user: -L and -p are incompatible\n"); return 1; } users_dir = opendir(USERS_DIRECTORY); if (users_dir == NULL) { fprintf(stderr, "user: no users directory\n"); return 1; } if (action == PASSWORD) { printf("TODO: interactively change password\n"); return 0; } if (sel == ALL) { struct dirent *de = NULL; while ((de = readdir(users_dir)) != NULL) { if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { continue; } user_print(user_read(de->d_name)); } return 0; } struct user *u = user_find(sel, name, uid); if (u == NULL) { fprintf(stderr, "user: not found\n"); return 1; } user_print(u); }