1/*
2 * Copyright 2008, Ingo Weinhold, ingo_weinhold@gmx.de. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 */
5
6#include <pwd.h>
7
8#include <errno.h>
9#include <string.h>
10#include <unistd.h>
11
12#include <new>
13
14#include <OS.h>
15
16#include <errno_private.h>
17#include <libroot_private.h>
18#include <RegistrarDefs.h>
19#include <user_group.h>
20
21#include <util/KMessage.h>
22
23
24using BPrivate::UserGroupLocker;
25using BPrivate::relocate_pointer;
26
27
28static KMessage sPasswdDBReply;
29static passwd** sPasswdEntries = NULL;
30static size_t sPasswdEntryCount = 0;
31static size_t sIterationIndex = 0;
32
33static struct passwd sPasswdBuffer;
34static char sPasswdStringBuffer[MAX_PASSWD_BUFFER_SIZE];
35
36
37static status_t
38query_passwd_entry(const char* name, uid_t _uid, struct passwd *passwd,
39	char *buffer, size_t bufferSize, struct passwd **_result)
40{
41	*_result = NULL;
42
43	KMessage message(BPrivate::B_REG_GET_USER);
44	if (name)
45		message.AddString("name", name);
46	else
47		message.AddInt32("uid", _uid);
48
49	KMessage reply;
50	status_t error = BPrivate::send_authentication_request_to_registrar(message,
51		reply);
52	if (error != B_OK)
53		return error == ENOENT ? B_OK : error;
54
55	int32 uid;
56	int32 gid;
57	const char* password;
58	const char* home;
59	const char* shell;
60	const char* realName;
61
62	if ((error = reply.FindInt32("uid", &uid)) != B_OK
63		|| (error = reply.FindInt32("gid", &gid)) != B_OK
64		|| (error = reply.FindString("name", &name)) != B_OK
65		|| (error = reply.FindString("password", &password)) != B_OK
66		|| (error = reply.FindString("home", &home)) != B_OK
67		|| (error = reply.FindString("shell", &shell)) != B_OK
68		|| (error = reply.FindString("real name", &realName)) != B_OK) {
69		return error;
70	}
71
72	error = BPrivate::copy_passwd_to_buffer(name, password, uid, gid, home,
73		shell, realName, passwd, buffer, bufferSize);
74	if (error == B_OK)
75		*_result = passwd;
76
77	return error;
78}
79
80
81static status_t
82init_passwd_db()
83{
84	if (sPasswdEntries != NULL)
85		return B_OK;
86
87	// ask the registrar
88	KMessage message(BPrivate::B_REG_GET_PASSWD_DB);
89	status_t error = BPrivate::send_authentication_request_to_registrar(message,
90		sPasswdDBReply);
91	if (error != B_OK)
92		return error;
93
94	// unpack the reply
95	int32 count;
96	passwd** entries;
97	int32 numBytes;
98	if ((error = sPasswdDBReply.FindInt32("count", &count)) != B_OK
99		|| (error = sPasswdDBReply.FindData("entries", B_RAW_TYPE,
100				(const void**)&entries, &numBytes)) != B_OK) {
101		return error;
102	}
103
104	// relocate the entries
105	addr_t baseAddress = (addr_t)entries;
106	for (int32 i = 0; i < count; i++) {
107		passwd* entry = relocate_pointer(baseAddress, entries[i]);
108		relocate_pointer(baseAddress, entry->pw_name);
109		relocate_pointer(baseAddress, entry->pw_passwd);
110		relocate_pointer(baseAddress, entry->pw_dir);
111		relocate_pointer(baseAddress, entry->pw_shell);
112		relocate_pointer(baseAddress, entry->pw_gecos);
113	}
114
115	sPasswdEntries = entries;
116	sPasswdEntryCount = count;
117
118	return B_OK;
119}
120
121
122// #pragma mark -
123
124
125struct passwd*
126getpwent(void)
127{
128	struct passwd* result = NULL;
129	int status = getpwent_r(&sPasswdBuffer, sPasswdStringBuffer,
130		sizeof(sPasswdStringBuffer), &result);
131	if (status != 0)
132		__set_errno(status);
133	return result;
134}
135
136
137int
138getpwent_r(struct passwd* passwd, char* buffer, size_t bufferSize,
139	struct passwd** _result)
140{
141	UserGroupLocker _;
142
143	int status = B_NO_MEMORY;
144
145	*_result = NULL;
146
147	if ((status = init_passwd_db()) == B_OK) {
148		if (sIterationIndex >= sPasswdEntryCount)
149			return ENOENT;
150
151		status = BPrivate::copy_passwd_to_buffer(
152			sPasswdEntries[sIterationIndex], passwd, buffer, bufferSize);
153
154		if (status == B_OK) {
155			sIterationIndex++;
156			*_result = passwd;
157		}
158	}
159
160	return status;
161}
162
163
164void
165setpwent(void)
166{
167	UserGroupLocker _;
168
169	sIterationIndex = 0;
170}
171
172
173void
174endpwent(void)
175{
176	UserGroupLocker locker;
177
178	sPasswdDBReply.Unset();
179	sPasswdEntries = NULL;
180	sPasswdEntryCount = 0;
181	sIterationIndex = 0;
182}
183
184
185struct passwd *
186getpwnam(const char *name)
187{
188	struct passwd* result = NULL;
189	int status = getpwnam_r(name, &sPasswdBuffer, sPasswdStringBuffer,
190		sizeof(sPasswdStringBuffer), &result);
191	if (status != 0)
192		__set_errno(status);
193	return result;
194}
195
196
197int
198getpwnam_r(const char *name, struct passwd *passwd, char *buffer,
199	size_t bufferSize, struct passwd **_result)
200{
201	return query_passwd_entry(name, 0, passwd, buffer, bufferSize, _result);
202}
203
204
205struct passwd *
206getpwuid(uid_t uid)
207{
208	struct passwd* result = NULL;
209	int status = getpwuid_r(uid, &sPasswdBuffer, sPasswdStringBuffer,
210		sizeof(sPasswdStringBuffer), &result);
211	if (status != 0)
212		__set_errno(status);
213	return result;
214}
215
216
217int
218getpwuid_r(uid_t uid, struct passwd *passwd, char *buffer,
219	size_t bufferSize, struct passwd **_result)
220{
221	return query_passwd_entry(NULL, uid, passwd, buffer, bufferSize, _result);
222}
223