1/*
2 * libwebsockets - small server side websockets and web server implementation
3 *
4 * Copyright (C) 2010 - 2019 Andy Green <andy@warmcat.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to
8 * deal in the Software without restriction, including without limitation the
9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
10 * sell copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
21 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
22 * IN THE SOFTWARE.
23 */
24
25#if !defined(_GNU_SOURCE)
26#define _GNU_SOURCE
27#endif
28#include "private-lib-core.h"
29
30#include <pwd.h>
31#include <grp.h>
32
33#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
34static void
35_lws_plat_apply_caps(unsigned int mode, const cap_value_t *cv, int count)
36{
37	cap_t caps;
38
39	if (!count)
40		return;
41
42	caps = cap_get_proc();
43
44	cap_set_flag(caps, (cap_flag_t)mode, count, cv, CAP_SET);
45	cap_set_proc(caps);
46	prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0);
47	cap_free(caps);
48}
49#endif
50
51int
52lws_plat_user_colon_group_to_ids(const char *u_colon_g, uid_t *puid, gid_t *pgid)
53{
54	char *colon = strchr(u_colon_g, ':'), u[33];
55	struct group *g;
56	struct passwd *p;
57	size_t ulen;
58
59	if (!colon)
60		return 1;
61
62	ulen = (size_t)(unsigned int)lws_ptr_diff(colon, u_colon_g);
63	if (ulen < 2 || ulen > sizeof(u) - 1)
64		return 1;
65
66	memcpy(u, u_colon_g, ulen);
67	u[ulen] = '\0';
68
69	colon++;
70
71#if defined(LWS_HAVE_GETGRNAM_R)
72	{
73		struct group gr;
74		char strs[1024];
75
76		if (getgrnam_r(colon, &gr, strs, sizeof(strs), &g) || !g) {
77#else
78	{
79		g = getgrnam(colon);
80		if (!g) {
81#endif
82			lwsl_err("%s: unknown group '%s'\n", __func__, colon);
83
84			return 1;
85		}
86		*pgid = g->gr_gid;
87	}
88
89#if defined(LWS_HAVE_GETPWNAM_R)
90	{
91		struct passwd pr;
92		char strs[1024];
93
94		if (getpwnam_r(u, &pr, strs, sizeof(strs), &p) || !p) {
95#else
96	{
97		p = getpwnam(u);
98		if (!p) {
99#endif
100			lwsl_err("%s: unknown user '%s'\n", __func__, u);
101
102			return 1;
103		}
104		*puid = p->pw_uid;
105	}
106
107	return 0;
108}
109
110int
111lws_plat_drop_app_privileges(struct lws_context *context, int actually_drop)
112{
113	struct passwd *p;
114	struct group *g;
115
116	/* if he gave us the groupname, align gid to match it */
117
118	if (context->groupname) {
119#if defined(LWS_HAVE_GETGRNAM_R)
120		struct group gr;
121		char strs[1024];
122
123		if (!getgrnam_r(context->groupname, &gr, strs, sizeof(strs), &g) && g) {
124#else
125		g = getgrnam(context->groupname);
126		if (g) {
127#endif
128			lwsl_cx_info(context, "group %s -> gid %u",
129				  context->groupname, g->gr_gid);
130			context->gid = g->gr_gid;
131		} else {
132			lwsl_cx_err(context, "unknown groupname '%s'",
133				 context->groupname);
134
135			return 1;
136		}
137	}
138
139	/* if he gave us the username, align uid to match it */
140
141	if (context->username) {
142#if defined(LWS_HAVE_GETPWNAM_R)
143		struct passwd pr;
144		char strs[1024];
145
146		if (!getpwnam_r(context->username, &pr, strs, sizeof(strs), &p) && p) {
147#else
148		p = getpwnam(context->username);
149		if (p) {
150#endif
151			context->uid = p->pw_uid;
152
153			lwsl_cx_info(context, "username %s -> uid %u",
154				  context->username, (unsigned int)p->pw_uid);
155		} else {
156			lwsl_cx_err(context, "unknown username %s",
157				 context->username);
158
159			return 1;
160		}
161	}
162
163	if (!actually_drop)
164		return 0;
165
166	/* if he gave us the gid or we have it from the groupname, set it */
167
168	if (context->gid && context->gid != (gid_t)-1l) {
169#if defined(LWS_HAVE_GETGRGID_R)
170		struct group gr;
171		char strs[1024];
172
173		if (getgrgid_r(context->gid, &gr, strs, sizeof(strs), &g) || !g) {
174#else
175		g = getgrgid(context->gid);
176		if (!g) {
177#endif
178			lwsl_cx_err(context, "cannot find name for gid %d",
179					context->gid);
180
181			return 1;
182		}
183
184		if (setgid(context->gid)) {
185			lwsl_cx_err(context, "setgid: %s failed",
186				    strerror(LWS_ERRNO));
187
188			return 1;
189		}
190
191		lwsl_cx_notice(context, "effective group '%s'", g->gr_name);
192	} else
193		lwsl_cx_info(context, "not changing group");
194
195
196	/* if he gave us the uid or we have it from the username, set it */
197
198	if (context->uid && context->uid != (uid_t)-1l) {
199#if defined(LWS_HAVE_GETPWUID_R)
200		struct passwd pr;
201		char strs[1024];
202
203		if (getpwuid_r(context->uid, &pr, strs, sizeof(strs), &p) || !p) {
204#else
205		p = getpwuid(context->uid);
206		if (!p) {
207#endif
208			lwsl_cx_err(context, "getpwuid: unable to find uid %d",
209				 context->uid);
210			return 1;
211		}
212
213#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
214		_lws_plat_apply_caps(CAP_PERMITTED, context->caps,
215				     context->count_caps);
216#endif
217
218		if (initgroups(p->pw_name,
219#if defined(__APPLE__)
220				(int)
221#endif
222				context->gid))
223			return 1;
224
225		if (setuid(context->uid)) {
226			lwsl_cx_err(context, "setuid: %s failed",
227				    strerror(LWS_ERRNO));
228
229			return 1;
230		} else
231			lwsl_cx_notice(context, "effective user '%s'",
232					p->pw_name);
233
234#if defined(LWS_HAVE_SYS_CAPABILITY_H) && defined(LWS_HAVE_LIBCAP)
235		_lws_plat_apply_caps(CAP_EFFECTIVE, context->caps,
236				     context->count_caps);
237
238		if (context->count_caps) {
239			int n;
240			for (n = 0; n < context->count_caps; n++)
241				lwsl_cx_notice(context, "   RETAINING CAP %d",
242					    (int)context->caps[n]);
243		}
244#endif
245	} else
246		lwsl_cx_info(context, "not changing user");
247
248	return 0;
249}
250