#include #include #include #include #include #include namespace { FILE *global_file; // Used by setpwent/getpwent/endpwent. bool open_global_file() { if(!global_file) { global_file = fopen("/etc/passwd", "r"); if(!global_file) { errno = EIO; return false; } } return true; } void close_global_file() { if(global_file) { fclose(global_file); global_file = nullptr; } } bool extract_entry(frg::string_view line, passwd *entry) { frg::string_view segments[8]; // Parse the line into 7 or 8 segments. size_t s = 0; int n; for(n = 0; n < 7; n++) { size_t d = line.find_first(':', s); if(d == size_t(-1)) break; segments[n] = line.sub_string(s, d - s); s = d + 1; } if(line.find_first(':', s) != size_t(-1)) return false; segments[n] = line.sub_string(s, line.size() - s); n++; if(n < 7) return false; // TODO: Handle strndup() failure. auto name = strndup(segments[0].data(), segments[0].size()); __ensure(name); auto passwd = strndup(segments[1].data(), segments[1].size()); __ensure(passwd); auto uid = segments[2].to_number(); if(!uid) return false; auto gid = segments[3].to_number(); if(!gid) return false; auto real_name = strndup(segments[4].data(), segments[4].size()); __ensure(real_name); auto dir = strndup(segments[5].data(), segments[5].size()); __ensure(dir); auto shell = strndup(segments[6].data(), segments[6].size()); __ensure(shell); // Chop the newline off the end of shell __ensure(strlen(shell) > 0); shell[strlen(shell) - 1] = '\0'; entry->pw_name = name; entry->pw_passwd = passwd; entry->pw_uid = *uid; entry->pw_gid = *gid; entry->pw_dir = dir; entry->pw_shell = shell; entry->pw_gecos = real_name; return true; } void copy_to_buffer(passwd *pwd, char *buffer, size_t size) { char *pw_dir = stpcpy(buffer, pwd->pw_name) + 1; free(pwd->pw_name); pwd->pw_name = buffer; char *pw_shell = stpcpy(pw_dir, pwd->pw_dir) + 1; free(pwd->pw_dir); pwd->pw_dir = pw_dir; char *pw_passwd = stpcpy(pw_shell, pwd->pw_shell) + 1; free(pwd->pw_shell); pwd->pw_shell = pw_shell; char *end = stpcpy(pw_passwd, pwd->pw_passwd); __ensure(end <= buffer + size); free(pwd->pw_passwd); pwd->pw_passwd = pw_passwd; } void clear_entry(passwd *entry) { free(entry->pw_name); free(entry->pw_dir); free(entry->pw_passwd); free(entry->pw_shell); entry->pw_name = nullptr; entry->pw_dir = nullptr; entry->pw_passwd = nullptr; entry->pw_shell = nullptr; } } struct passwd *getpwent(void) { static passwd entry; char line[NSS_BUFLEN_PASSWD]; if(!open_global_file()) { return nullptr; } if (fgets(line, NSS_BUFLEN_PASSWD, global_file)) { clear_entry(&entry); if(!extract_entry(line, &entry)) { errno = EINVAL; // I suppose this can be a valid errno? return nullptr; } return &entry; } if(ferror(global_file)) { errno = EIO; } return nullptr; } struct passwd *getpwnam(const char *name) { static passwd entry; auto file = fopen("/etc/passwd", "r"); if(!file) return nullptr; char line[NSS_BUFLEN_PASSWD]; while(fgets(line, NSS_BUFLEN_PASSWD, file)) { clear_entry(&entry); if(!extract_entry(line, &entry)) continue; if(!strcmp(entry.pw_name, name)) { fclose(file); return &entry; } } int err = errno; if(ferror(file)) { err = EIO; } fclose(file); errno = err; return nullptr; } int getpwnam_r(const char *name, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) { *result = nullptr; auto file = fopen("/etc/passwd", "r"); if(!file) { return EIO; } char line[NSS_BUFLEN_PASSWD]; while(fgets(line, NSS_BUFLEN_PASSWD, file)) { if(!extract_entry(line, pwd)) continue; if(!strcmp(pwd->pw_name, name)) { fclose(file); size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir) + strlen(pwd->pw_shell) + strlen(pwd->pw_passwd) + 4; if (size < required_size) return ERANGE; copy_to_buffer(pwd, buffer, size); *result = pwd; return 0; } } int ret = 0; if(ferror(file)) { ret = EIO; } fclose(file); return ret; } struct passwd *getpwuid(uid_t uid) { static passwd entry; auto file = fopen("/etc/passwd", "r"); if(!file) return nullptr; char line[NSS_BUFLEN_PASSWD]; while(fgets(line, NSS_BUFLEN_PASSWD, file)) { clear_entry(&entry); if(!extract_entry(line, &entry)) continue; if(entry.pw_uid == uid) { fclose(file); return &entry; } } int err = ESRCH; if(ferror(file)) { err = EIO; } fclose(file); errno = err; return nullptr; } int getpwuid_r(uid_t uid, struct passwd *pwd, char *buffer, size_t size, struct passwd **result) { *result = nullptr; auto file = fopen("/etc/passwd", "r"); if(!file) { return EIO; } char line[NSS_BUFLEN_PASSWD]; while(fgets(line, NSS_BUFLEN_PASSWD, file)) { if(!extract_entry(line, pwd)) continue; if(pwd->pw_uid == uid) { fclose(file); size_t required_size = strlen(pwd->pw_name) + strlen(pwd->pw_dir) + strlen(pwd->pw_shell) + + strlen(pwd->pw_passwd) + 4; if (size < required_size) return ERANGE; copy_to_buffer(pwd, buffer, size); *result = pwd; return 0; } } int ret = 0; if(ferror(file)) { ret = EIO; } fclose(file); return ret; } void setpwent(void) { if(!open_global_file()) { return; } rewind(global_file); } void endpwent(void) { close_global_file(); } int putpwent(const struct passwd *, FILE *) { __ensure(!"Not implemented"); __builtin_unreachable(); } struct passwd *fgetpwent(FILE *) { __ensure(!"Not implemented"); __builtin_unreachable(); }