Files
semi-libc/src/passwd/account_client.h
T
2026-07-18 18:32:16 +08:00

405 lines
11 KiB
C

#include <pwd.h>
#include <grp.h>
#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <stddef.h>
#include <stdio.h>
#include "spp.h"
#include "json.h"
#ifndef _ACCOUNT_CLIENT
#define _ACCOUNT_CLIENT
#define REQ2(condition, err) if (!(condition)) { errno = err; return -1; }
#define REQ(condition) REQ2(condition, EPERM)
#define REQP2(condition, err) if (!(condition)) { errno = err; return NULL; }
#define REQP(condition) REQP2(condition, EPERM)
#define ITEROBJ(obj, it) for (struct json_object_element_s *it = obj->start; it != NULL; it = it->next)
#define DECLPOOL(pool, buf, size) struct account_memory_pool pool = { .ptr = buf, .len = size, .cursor = 0 }
#ifdef __linux__
static inline int account_connect() {
return spp_connect_linux("verified:org.semilabs.os/accountd");
}
#else
static inline int account_connect() {
return spp_connect_unix("/var/run/accountd.sock");
}
#endif /* __linux__ */
struct account_memory_pool {
void *ptr;
size_t cursor;
size_t len;
};
static inline void *account_allocate_pool(struct account_memory_pool *pool, size_t size) {
if (!pool || !pool->ptr || size == 0) {
return NULL;
}
if (pool->cursor + size > pool->len) {
return NULL;
}
void *result = (char *)pool->ptr + pool->cursor;
pool->cursor += size;
return result;
}
static inline int account_recordpw(
struct json_value_s *root,
struct passwd *passwd,
struct account_memory_pool *pool
) {
passwd->pw_passwd = account_allocate_pool(pool, 2);
REQ2(passwd->pw_passwd, ERANGE);
strcpy(passwd->pw_passwd, "x");
passwd->pw_gecos = account_allocate_pool(pool, 1);
REQ2(passwd->pw_gecos, ERANGE);
strcpy(passwd->pw_gecos, "");
struct json_object_s *object = json_value_as_object(root);
REQ(object);
ITEROBJ(object, it) {
const char *name = it->name->string;
struct json_value_s *value = it->value;
if (strcmp(name, "username") == 0) {
struct json_string_s *username = json_value_as_string(value);
REQ(username);
passwd->pw_name = account_allocate_pool(pool, username->string_size + 1);
REQ2(passwd->pw_name, ERANGE);
strcpy(passwd->pw_name, username->string);
continue;
}
if (strcmp(name, "home_directory") == 0) {
struct json_string_s *homedir = json_value_as_string(value);
REQ(homedir);
passwd->pw_dir = account_allocate_pool(pool, homedir->string_size + 1);
REQ2(passwd->pw_dir, ERANGE);
strcpy(passwd->pw_dir, homedir->string);
continue;
}
if (strcmp(name, "defaults") == 0) {
struct json_object_s *defaults = json_value_as_object(value);
REQ(defaults);
ITEROBJ(defaults, def) {
name = def->name->string;
value = def->value;
if (strcmp(name, "CliLoginShell") == 0) {
struct json_string_s *shell = json_value_as_string(value);
REQ(shell);
passwd->pw_shell = account_allocate_pool(pool, shell->string_size + 1);
REQ2(passwd->pw_shell, ERANGE);
strcpy(passwd->pw_shell, shell->string);
continue;
}
}
continue;
}
if (strcmp(name, "host_uid") == 0) {
struct json_number_s *uid = json_value_as_number(value);
REQ(uid);
passwd->pw_uid = atoi(uid->number);
continue;
}
}
return 0;
}
static inline int account_recordgr(
struct json_value_s *root,
struct group *group,
struct account_memory_pool *pool
) {
group->gr_passwd = account_allocate_pool(pool, 2);
REQ2(group->gr_passwd, ERANGE);
strcpy(group->gr_passwd, "x");
struct json_object_s *object = json_value_as_object(root);
REQ(object);
ITEROBJ(object, it) {
const char *name = it->name->string;
struct json_value_s *value = it->value;
if (strcmp(name, "groupname") == 0) {
struct json_string_s *groupname = json_value_as_string(value);
REQ(groupname);
group->gr_name = account_allocate_pool(pool, groupname->string_size + 1);
REQ2(group->gr_name, ERANGE);
strcpy(group->gr_name, groupname->string);
continue;
}
if (strcmp(name, "host_gid") == 0) {
struct json_number_s *gid = json_value_as_number(value);
REQ(gid);
group->gr_gid = atoi(gid->number);
continue;
}
}
return 0;
}
typedef int (*pwgr_parser_t)(struct json_value_s*, void*, struct account_memory_pool*);
static inline int account_getpwgrkey(
int fd,
const char *method,
const char *key,
const char *valueborder,
const char *name,
pwgr_parser_t parser,
void *pwgr,
struct account_memory_pool *pool
) {
int status;
const char *request_template = "{\"method\": \"%s\", \"params\": {\"%s\": %s%s%s}}";
int request_need = snprintf(NULL, 0, request_template, method, key, valueborder, name, valueborder);
REQ(request_need > 0);
char *request_json = malloc(request_need + 1);
REQ2(request_json, ENOMEM);
snprintf(request_json, request_need + 1, request_template, method, key, valueborder, name, valueborder);
status = spp_send(fd, request_json);
free(request_json);
REQ(status == 0);
const char *reply_str = spp_recv(fd);
REQ(reply_str);
struct json_value_s *reply = json_parse(reply_str, strlen(reply_str));
spp_free((void*)reply_str);
REQ(reply);
struct json_object_s *reply_object = json_value_as_object(reply);
if (!reply_object) goto format_error;
ITEROBJ(reply_object, it) {
if (strcmp(it->name->string, "success") == 0) {
if (json_value_is_false(it->value)) goto not_found;
continue;
}
if (strcmp(it->name->string, "value") == 0) {
status = parser(it->value, pwgr, pool);
if (status != 0) goto not_found;
continue;
}
}
free(reply);
return 0;
format_error:
errno = EPERM;
not_found:
free(reply);
return -1;
}
static inline char *account_list_uuid_next(
int fd,
const char *method,
unsigned int no
) {
int status;
const char *request_template = "{\"method\": \"%s\", \"params\": {\"start\": %u, \"len\": 1}}";
int request_need = snprintf(NULL, 0, request_template, method, no);
REQP(request_need > 0);
char *request_json = malloc(request_need + 1);
REQP2(request_json, ENOMEM);
snprintf(request_json, request_need + 1, request_template, method, no);
status = spp_send(fd, request_json);
free(request_json);
REQP(status == 0);
const char *reply_str = spp_recv(fd);
REQP(reply_str);
struct json_value_s *reply = json_parse(reply_str, strlen(reply_str));
spp_free((void*)reply_str);
REQP(reply);
struct json_object_s *reply_object = json_value_as_object(reply);
if (!reply_object) goto format_error;
ITEROBJ(reply_object, it) {
if (strcmp(it->name->string, "success") == 0) {
if (json_value_is_false(it->value)) goto not_found;
continue;
}
if (strcmp(it->name->string, "value") == 0) {
struct json_array_s *list = json_value_as_array(it->value);
if (!list) goto not_found;
struct json_array_element_s *first = list->start;
if (!first) goto not_found;
struct json_string_s *item = json_value_as_string(first->value);
if (!item) goto format_error;
return strdup(item->string);
}
}
free(reply);
return NULL;
format_error:
errno = EPERM;
not_found:
free(reply);
return NULL;
}
static inline int account_getpwuid(
int fd,
uid_t uid,
struct passwd *passwd,
struct account_memory_pool *pool
) {
char uid_str[32];
int len = snprintf(uid_str, sizeof(uid_str), "%u", (unsigned int)uid);
REQ(len > 0 && (size_t)len < sizeof(uid_str));
return account_getpwgrkey(
fd,
"GetUserInfo",
"host_uid",
"",
uid_str,
(pwgr_parser_t)account_recordpw,
passwd,
pool
);
}
static inline int account_getpwnam(
int fd,
const char *name,
struct passwd *passwd,
struct account_memory_pool *pool
) {
return account_getpwgrkey(
fd,
"GetUserInfo",
"username",
"\"",
name,
(pwgr_parser_t)account_recordpw,
passwd,
pool
);
}
static inline int account_getpwuuid(
int fd,
const char *uuid,
struct passwd *passwd,
struct account_memory_pool *pool
) {
return account_getpwgrkey(
fd,
"GetUserInfo",
"uuid",
"\"",
uuid,
(pwgr_parser_t)account_recordpw,
passwd,
pool
);
}
static inline int account_getgrgid(
int fd,
gid_t gid,
struct group *group,
struct account_memory_pool *pool
) {
char gid_str[32];
int len = snprintf(gid_str, sizeof(gid_str), "%u", (unsigned int)gid);
REQ(len > 0 && (size_t)len < sizeof(gid_str));
return account_getpwgrkey(
fd,
"GetGroupInfo",
"host_gid",
"",
gid_str,
(pwgr_parser_t)account_recordgr,
group,
pool
);
}
static inline int account_getgrnam(
int fd,
const char *name,
struct group *group,
struct account_memory_pool *pool
) {
return account_getpwgrkey(
fd,
"GetGroupInfo",
"groupname",
"\"",
name,
(pwgr_parser_t)account_recordgr,
group,
pool
);
}
static inline int account_getgruuid(
int fd,
const char *uuid,
struct group *group,
struct account_memory_pool *pool
) {
return account_getpwgrkey(
fd,
"GetGroupInfo",
"uuid",
"\"",
uuid,
(pwgr_parser_t)account_recordgr,
group,
pool
);
}
static inline int account_getpwent(
int fd,
unsigned int *no,
struct passwd *passwd,
struct account_memory_pool *pool
) {
int status;
char *next_uuid = account_list_uuid_next(fd, "ListUser", *no);
REQ2(next_uuid, errno);
status = account_getpwuuid(fd, next_uuid, passwd, pool);
free(next_uuid);
REQ(status == 0);
*no += 1;
return 0;
}
static inline int account_getgrent(
int fd,
unsigned int *no,
struct group *group,
struct account_memory_pool *pool
) {
int status;
char *next_uuid = account_list_uuid_next(fd, "ListGroup", *no);
REQ2(next_uuid, errno);
status = account_getgruuid(fd, next_uuid, group, pool);
free(next_uuid);
REQ(status == 0);
*no += 1;
return 0;
}
#endif /* _ACCOUNT_CLIENT */