1/*
2 * Copyright 2012 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 *		Paweł Dziepak, pdziepak@quarnos.org
7 */
8
9
10#include "IdMapper.h"
11
12#include <grp.h>
13#include <pwd.h>
14#include <stdio.h>
15#include <stdlib.h>
16#include <string.h>
17#include <sys/types.h>
18
19#include <File.h>
20#include <FindDirectory.h>
21#include <OS.h>
22#include <Path.h>
23
24
25port_id		gRequestPort;
26port_id		gReplyPort;
27
28const char*	kNobodyName		= "nobody";
29uid_t		gNobodyId;
30
31const char*	kNogroupName	= "nobody";
32uid_t		gNogroupId;
33
34const char* gDomainName		= "localdomain";
35
36
37status_t
38SendError(status_t error)
39{
40	return write_port(gReplyPort, MsgError, &error, sizeof(error));
41}
42
43
44status_t
45MatchDomain(char* name)
46{
47	char* domain = strchr(name, '@');
48	if (domain == NULL)
49		return B_MISMATCHED_VALUES;
50
51	if (strcmp(domain + 1, gDomainName) != 0)
52		return B_BAD_VALUE;
53
54	*domain = '\0';
55
56	return B_OK;
57}
58
59
60char*
61AddDomain(const char* name)
62{
63	uint32 fullLength = strlen(name) + strlen(gDomainName) + 2;
64	char* fullName = reinterpret_cast<char*>(malloc(fullLength));
65	if (fullName == NULL)
66		return NULL;
67
68	strcpy(fullName, name);
69	strcat(fullName, "@");
70	strcat(fullName, gDomainName);
71
72	return fullName;
73}
74
75
76status_t
77NameToUID(void* buffer)
78{
79	char* userName = reinterpret_cast<char*>(buffer);
80
81	struct passwd* userInfo = NULL;
82
83	if (MatchDomain(userName) == B_OK)
84		userInfo = getpwnam(userName);
85
86	if (userInfo == NULL)
87		return write_port(gReplyPort, MsgReply, &gNobodyId, sizeof(gNobodyId));
88
89	return write_port(gReplyPort, MsgReply, &userInfo->pw_uid, sizeof(uid_t));
90}
91
92
93status_t
94UIDToName(void* buffer)
95{
96	uid_t userId = *reinterpret_cast<uid_t*>(buffer);
97
98	const char* name = NULL;
99
100	struct passwd* userInfo = getpwuid(userId);
101	if (userInfo != NULL) {
102		name = userInfo->pw_name;
103		name = AddDomain(name);
104	}
105
106	status_t result;
107
108	if (name != NULL) {
109		result = write_port(gReplyPort, MsgReply, name, strlen(name) + 1);
110		free(const_cast<char*>(name));
111	} else {
112		result = write_port(gReplyPort, MsgReply, kNobodyName,
113			strlen(kNobodyName) + 1);
114	}
115
116	return result;
117}
118
119
120status_t
121NameToGID(void* buffer)
122{
123	char* groupName = reinterpret_cast<char*>(buffer);
124
125	struct group* groupInfo = NULL;
126
127	if (MatchDomain(groupName) == B_OK)
128		groupInfo = getgrnam(groupName);
129
130	if (groupInfo == NULL) {
131		return write_port(gReplyPort, MsgReply, &gNogroupId,
132			sizeof(gNogroupId));
133	}
134
135	return write_port(gReplyPort, MsgReply, &groupInfo->gr_gid, sizeof(gid_t));
136}
137
138
139status_t
140GIDToName(void* buffer)
141{
142	gid_t groupId = *reinterpret_cast<gid_t*>(buffer);
143
144	const char* name = NULL;
145
146	struct group* groupInfo = getgrgid(groupId);
147	if (groupInfo != NULL) {
148		name = groupInfo->gr_name;
149		name = AddDomain(name);
150	}
151
152	status_t result;
153
154	if (name != NULL) {
155		result = write_port(gReplyPort, MsgReply, name, strlen(name) + 1);
156		free(const_cast<char*>(name));
157	} else {
158		result = write_port(gReplyPort, MsgReply, kNogroupName,
159			strlen(kNogroupName) + 1);
160	}
161
162	return result;
163}
164
165
166status_t
167ParseRequest(int32 code, void* buffer)
168{
169	switch (code) {
170		case MsgNameToUID:
171			return NameToUID(buffer);
172
173		case MsgUIDToName:
174			return UIDToName(buffer);
175
176		case MsgNameToGID:
177			return NameToGID(buffer);
178
179		case MsgGIDToName:
180			return GIDToName(buffer);
181
182		default:
183			return SendError(B_BAD_VALUE);
184	}
185}
186
187
188status_t
189MainLoop()
190{
191	do {
192		ssize_t size = port_buffer_size(gRequestPort);
193		if (size < B_OK)
194			return 0;
195
196		void* buffer = malloc(size);
197		if (buffer == NULL)
198			return B_NO_MEMORY;
199
200		int32 code;
201		size = read_port(gRequestPort, &code, buffer, size);
202		if (size < B_OK) {
203			free(buffer);
204			return 0;
205		}
206
207		status_t result = ParseRequest(code, buffer);
208		free(buffer);
209
210		if (result != B_OK)
211			return 0;
212
213	} while (true);
214}
215
216
217status_t
218ReadSettings()
219{
220	BPath path;
221	status_t result = find_directory(B_SYSTEM_SETTINGS_DIRECTORY, &path);
222	if (result != B_OK)
223		return result;
224	result = path.Append("nfs4_idmapper.conf");
225	if (result != B_OK)
226		return result;
227
228	BFile file(path.Path(), B_READ_ONLY);
229	if (file.InitCheck() != B_OK)
230		return file.InitCheck();
231
232	off_t size;
233	result = file.GetSize(&size);
234	if (result != B_OK)
235		return result;
236
237	void* buffer = malloc(size);
238	if (buffer == NULL)
239		return B_NO_MEMORY;
240
241	file.Read(buffer, size);
242
243	gDomainName = reinterpret_cast<char*>(buffer);
244
245	return B_OK;
246}
247
248
249int
250main(int argc, char** argv)
251{
252	gRequestPort = find_port(kRequestPortName);
253	if (gRequestPort < B_OK) {
254		fprintf(stderr, "%s\n", strerror(gRequestPort));
255		return gRequestPort;
256	}
257
258	gReplyPort = find_port(kReplyPortName);
259	if (gReplyPort < B_OK) {
260		fprintf(stderr, "%s\n", strerror(gReplyPort));
261		return gReplyPort;
262	}
263
264	ReadSettings();
265
266	struct passwd* userInfo = getpwnam(kNobodyName);
267	if (userInfo != NULL)
268		gNobodyId = userInfo->pw_uid;
269	else
270		gNobodyId = 0;
271
272	struct group* groupInfo = getgrnam(kNogroupName);
273	if (groupInfo != NULL)
274		gNogroupId = groupInfo->gr_gid;
275	else
276		gNogroupId = 0;
277
278	return MainLoop();
279}
280
281