summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJakob Kaivo <jkk@ungol.org>2020-04-01 03:01:09 -0400
committerJakob Kaivo <jkk@ungol.org>2020-04-01 03:01:09 -0400
commitcbd712a43b8702517a1b4cbe50df6a0b8a39bae6 (patch)
tree410c88e1e204725d6b22c9a2b541f55b2ec4a168
parentde78c9dcb6396eae52ff5988695ad4749268ffc8 (diff)
initial pass with some hard-coded values
-rw-r--r--user.c164
1 files changed, 163 insertions, 1 deletions
diff --git a/user.c b/user.c
index 9a019a7..9eaa7e1 100644
--- a/user.c
+++ b/user.c
@@ -20,4 +20,166 @@
* 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.
- */ \ No newline at end of file
+ */
+
+#define _POSIX_C_SOURCE 200809L
+#include <fcntl.h>
+#include <dirent.h>
+#include <pwd.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#define USERS_DIRECTORY "/var/secure/users"
+static DIR *users_dir = NULL;
+
+struct user {
+ struct passwd pw;
+ char *gecos;
+ char *password;
+};
+
+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
+ u.gecos = name; // TODO: read file at userfd, gecos
+ 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);
+}