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 <shadow.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 <AutoDeleter.h>
17#include <errno_private.h>
18#include <libroot_private.h>
19#include <RegistrarDefs.h>
20#include <user_group.h>
21
22#include <util/KMessage.h>
23
24
25using BPrivate::UserGroupLocker;
26using BPrivate::relocate_pointer;
27
28
29static KMessage sShadowPwdDBReply;
30static spwd** sShadowPwdEntries = NULL;
31static size_t sShadowPwdEntryCount = 0;
32static size_t sIterationIndex = 0;
33
34static struct spwd sShadowPwdBuffer;
35static char sShadowPwdStringBuffer[MAX_SHADOW_PWD_BUFFER_SIZE];
36
37
38static status_t
39init_shadow_pwd_db()
40{
41	if (sShadowPwdEntries != NULL)
42		return B_OK;
43
44	// ask the registrar
45	KMessage message(BPrivate::B_REG_GET_SHADOW_PASSWD_DB);
46	status_t error = BPrivate::send_authentication_request_to_registrar(message,
47		sShadowPwdDBReply);
48	if (error != B_OK)
49		return error;
50
51	// unpack the reply
52	int32 count;
53	spwd** entries;
54	int32 numBytes;
55	if ((error = sShadowPwdDBReply.FindInt32("count", &count)) != B_OK
56		|| (error = sShadowPwdDBReply.FindData("entries", B_RAW_TYPE,
57				(const void**)&entries, &numBytes)) != B_OK) {
58		return error;
59	}
60
61	// relocate the entries
62	addr_t baseAddress = (addr_t)entries;
63	for (int32 i = 0; i < count; i++) {
64		spwd* entry = relocate_pointer(baseAddress, entries[i]);
65		relocate_pointer(baseAddress, entry->sp_namp);
66		relocate_pointer(baseAddress, entry->sp_pwdp);
67	}
68
69	sShadowPwdEntries = entries;
70	sShadowPwdEntryCount = count;
71
72	return B_OK;
73}
74
75
76// #pragma mark -
77
78
79struct spwd*
80getspent(void)
81{
82	struct spwd* result = NULL;
83	int status = getspent_r(&sShadowPwdBuffer, sShadowPwdStringBuffer,
84		sizeof(sShadowPwdStringBuffer), &result);
85	if (status != 0)
86		__set_errno(status);
87	return result;
88}
89
90
91int
92getspent_r(struct spwd* spwd, char* buffer, size_t bufferSize,
93	struct spwd** _result)
94{
95	UserGroupLocker _;
96
97	int status = B_NO_MEMORY;
98
99	*_result = NULL;
100
101	if ((status = init_shadow_pwd_db()) == B_OK) {
102		if (sIterationIndex >= sShadowPwdEntryCount)
103			return ENOENT;
104
105		status = BPrivate::copy_shadow_pwd_to_buffer(
106			sShadowPwdEntries[sIterationIndex], spwd, buffer, bufferSize);
107
108		if (status == B_OK) {
109			sIterationIndex++;
110			*_result = spwd;
111		}
112	}
113
114	return status;
115}
116
117
118void
119setspent(void)
120{
121	UserGroupLocker _;
122
123	sIterationIndex = 0;
124}
125
126
127void
128endspent(void)
129{
130	UserGroupLocker locker;
131
132	sShadowPwdDBReply.Unset();
133	sShadowPwdEntries = NULL;
134	sShadowPwdEntryCount = 0;
135	sIterationIndex = 0;
136}
137
138
139struct spwd *
140getspnam(const char *name)
141{
142	struct spwd* result = NULL;
143	int status = getspnam_r(name, &sShadowPwdBuffer, sShadowPwdStringBuffer,
144		sizeof(sShadowPwdStringBuffer), &result);
145	if (status != 0)
146		__set_errno(status);
147	return result;
148}
149
150
151int
152getspnam_r(const char *name, struct spwd *spwd, char *buffer,
153	size_t bufferSize, struct spwd **_result)
154{
155	*_result = NULL;
156
157	KMessage message(BPrivate::B_REG_GET_USER);
158	message.AddString("name", name);
159	message.AddBool("shadow", true);
160
161	KMessage reply;
162	status_t error = BPrivate::send_authentication_request_to_registrar(message,
163		reply);
164	if (error != B_OK)
165		return error;
166
167	const char* password;
168	int32 lastChanged;
169	int32 min;
170	int32 max;
171	int32 warn;
172	int32 inactive;
173	int32 expiration;
174	int32 flags;
175
176	if ((error = reply.FindString("name", &name)) != B_OK
177		|| (error = reply.FindString("shadow password", &password)) != B_OK
178		|| (error = reply.FindInt32("last changed", &lastChanged)) != B_OK
179		|| (error = reply.FindInt32("min", &min)) != B_OK
180		|| (error = reply.FindInt32("max", &max)) != B_OK
181		|| (error = reply.FindInt32("warn", &warn)) != B_OK
182		|| (error = reply.FindInt32("inactive", &inactive)) != B_OK
183		|| (error = reply.FindInt32("expiration", &expiration)) != B_OK
184		|| (error = reply.FindInt32("flags", &flags)) != B_OK) {
185		return error;
186	}
187
188	error = BPrivate::copy_shadow_pwd_to_buffer(name, password, lastChanged,
189		min, max, warn, inactive, expiration, flags, spwd, buffer, bufferSize);
190	if (error == B_OK)
191		*_result = spwd;
192
193	return error;
194}
195
196
197struct spwd*
198sgetspent(const char* line)
199{
200	struct spwd* result = NULL;
201	int status = sgetspent_r(line, &sShadowPwdBuffer, sShadowPwdStringBuffer,
202		sizeof(sShadowPwdStringBuffer), &result);
203	if (status != 0)
204		__set_errno(status);
205	return result;
206}
207
208
209int
210sgetspent_r(const char* _line, struct spwd *spwd, char *buffer,
211	size_t bufferSize, struct spwd** _result)
212{
213	*_result = NULL;
214
215	if (_line == NULL)
216		return B_BAD_VALUE;
217
218	// we need a mutable copy of the line
219	char* line = strdup(_line);
220	if (line == NULL)
221		return B_NO_MEMORY;
222	MemoryDeleter _(line);
223
224	char* name;
225	char* password;
226	int lastChanged;
227	int min;
228	int max;
229	int warn;
230	int inactive;
231	int expiration;
232	int flags;
233
234	status_t status = BPrivate::parse_shadow_pwd_line(line, name, password,
235		lastChanged, min, max, warn, inactive, expiration, flags);
236	if (status != B_OK)
237		return status;
238
239	status = BPrivate::copy_shadow_pwd_to_buffer(name, password, lastChanged,
240		min, max, warn, inactive, expiration, flags, spwd, buffer, bufferSize);
241	if (status != B_OK)
242		return status;
243
244	*_result = spwd;
245	return 0;
246}
247
248
249struct spwd*
250fgetspent(FILE* file)
251{
252	struct spwd* result = NULL;
253	int status = fgetspent_r(file, &sShadowPwdBuffer, sShadowPwdStringBuffer,
254		sizeof(sShadowPwdStringBuffer), &result);
255	if (status != 0)
256		__set_errno(status);
257	return result;
258}
259
260
261int
262fgetspent_r(FILE* file, struct spwd* spwd, char* buffer, size_t bufferSize,
263	struct spwd** _result)
264{
265	*_result = NULL;
266
267	// read a line
268	char lineBuffer[LINE_MAX + 1];
269	__set_errno(0);
270	char* line = fgets(lineBuffer, sizeof(lineBuffer), file);
271	if (line == NULL) {
272		if (errno != 0)
273			return errno;
274		return ENOENT;
275	}
276
277	return sgetspent_r(line, spwd, buffer, bufferSize, _result);
278}
279