1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* gkr-pam-client.h - Simple code for communicating with daemon
3 
4    Copyright (C) 2007 Stef Walter
5 
6    The Gnome Keyring Library is free software; you can redistribute it and/or
7    modify it under the terms of the GNU Library General Public License as
8    published by the Free Software Foundation; either version 2 of the
9    License, or (at your option) any later version.
10 
11    The Gnome Keyring Library is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14    Library General Public License for more details.
15 
16    You should have received a copy of the GNU Library General Public
17    License along with the Gnome Library; see the file COPYING.LIB.  If not,
18    <http://www.gnu.org/licenses/>.
19 
20    Author: Stef Walter <stef@memberwebs.com>
21 */
22 
23 #include "config.h"
24 
25 #include "gkr-pam.h"
26 
27 #include "egg/egg-buffer.h"
28 #include "egg/egg-unix-credentials.h"
29 
30 #include "daemon/control/gkd-control-codes.h"
31 
32 #include <sys/types.h>
33 #include <sys/param.h>
34 #include <sys/socket.h>
35 #include <sys/stat.h>
36 #include <sys/un.h>
37 #include <sys/uio.h>
38 #include <sys/wait.h>
39 
40 #include <assert.h>
41 #include <errno.h>
42 #include <fcntl.h>
43 #include <signal.h>
44 #include <stdio.h>
45 #include <stdlib.h>
46 #include <string.h>
47 #include <syslog.h>
48 #include <unistd.h>
49 
50 #if defined(HAVE_GETPEERUCRED)
51 #include <ucred.h>
52 #endif
53 
54 #if defined(LOCAL_PEERCRED)
55 #include <sys/param.h>
56 #include <sys/ucred.h>
57 #endif
58 
59 static int
check_peer_same_uid(struct passwd * pwd,int sock)60 check_peer_same_uid (struct passwd *pwd,
61                      int sock)
62 {
63 	uid_t uid = -1;
64 
65 	/*
66 	 * Certain OS require a message to be sent over the unix socket for the
67 	 * otherside to get the process credentials. Most uncool.
68 	 *
69 	 * The normal gnome-keyring protocol accomodates this and the client
70 	 * sends a message/byte before sending anything else. This only works
71 	 * for the daemon verifying the client.
72 	 *
73 	 * This code here is used by a client to verify the daemon is running
74 	 * as the right user. Since we cannot modify the protocol, this only
75 	 * works on OSs that can do this credentials lookup transparently.
76 	 */
77 
78 /* Linux */
79 #if defined(SO_PEERCRED)
80 	struct ucred cr;
81 	socklen_t cr_len = sizeof (cr);
82 
83 	if (getsockopt (sock, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
84 	    cr_len == sizeof (cr)) {
85 	    	uid = cr.uid;
86 	} else {
87 		syslog (GKR_LOG_ERR, "could not get gnome-keyring-daemon socket credentials, "
88 		        "(returned len %d/%d)\n", cr_len, (int) sizeof (cr));
89 		return -1;
90 	}
91 
92 
93 /* The BSDs */
94 #elif defined(LOCAL_PEERCRED)
95 	uid_t gid;
96         struct xucred xuc;
97         socklen_t xuc_len = sizeof (xuc);
98 
99 	if (getsockopt (sock, 0, LOCAL_PEERCRED, &xuc, &xuc_len) == 0 &&
100 	    xuc.cr_version == XUCRED_VERSION) {
101 	    	uid = xuc.cr_uid;
102 	} else {
103 		syslog (GKR_LOG_ERR, "could not get gnome-keyring-daemon socket credentials, "
104 		        "(returned version %d/%d)\n", xuc.cr_version, XUCRED_VERSION);
105 		return -1;
106 	}
107 
108 
109 /* NOTE: Add more here */
110 #else
111 	syslog (GKR_LOG_WARN, "Cannot verify that the process to which we are passing the login"
112 	        " password is genuinely running as the same user login: not supported on this OS.");
113 	uid = pwd->pw_uid;
114 
115 
116 #endif
117 
118 	if (uid != pwd->pw_uid) {
119 		syslog (GKR_LOG_ERR, "The gnome keyring socket is not running with the same "
120 		        "credentials as the user login. Disconnecting.");
121 		return 0;
122 	}
123 
124 	return 1;
125 }
126 
127 static int
write_credentials_byte(int sock)128 write_credentials_byte (int sock)
129 {
130 	for (;;) {
131 		if (egg_unix_credentials_write (sock) < 0) {
132 			if (errno == EINTR || errno == EAGAIN)
133 				continue;
134 			syslog (GKR_LOG_ERR, "couldn't send credentials to daemon: %s",
135 			        strerror (errno));
136 			return -1;
137 		}
138 
139 		break;
140 	}
141 
142 	return 0;
143 }
144 
145 static int
lookup_daemon(struct passwd * pwd,const char * control,struct sockaddr_un * addr)146 lookup_daemon (struct passwd *pwd,
147                const char *control,
148                struct sockaddr_un *addr)
149 {
150 	struct stat st;
151 
152 	if (strlen (control) + 1 > sizeof (addr->sun_path)) {
153 		syslog (GKR_LOG_ERR, "gkr-pam: address is too long for unix socket path: %s",
154 		        control);
155 		return GKD_CONTROL_RESULT_FAILED;
156 	}
157 
158 	memset (addr, 0, sizeof (*addr));
159 	addr->sun_family = AF_UNIX;
160 	strcpy (addr->sun_path, control);
161 
162 	/* A bunch of checks to make sure nothing funny is going on */
163 	if (lstat (addr->sun_path, &st) < 0) {
164 		if (errno == ENOENT)
165 			return GKD_CONTROL_RESULT_NO_DAEMON;
166 
167 		syslog (GKR_LOG_ERR, "Couldn't access gnome keyring socket: %s: %s",
168 		        addr->sun_path, strerror (errno));
169 		return GKD_CONTROL_RESULT_FAILED;
170 	}
171 
172 	if (st.st_uid != pwd->pw_uid) {
173 		syslog (GKR_LOG_ERR, "The gnome keyring socket is not owned with the same "
174 		        "credentials as the user login: %s", addr->sun_path);
175 		return GKD_CONTROL_RESULT_FAILED;
176 	}
177 
178 	if (S_ISLNK(st.st_mode) || !S_ISSOCK(st.st_mode)) {
179 		syslog (GKR_LOG_ERR, "The gnome keyring socket is not a valid simple "
180 		        "non-linked socket");
181 		return GKD_CONTROL_RESULT_FAILED;
182 	}
183 
184 	return GKD_CONTROL_RESULT_OK;
185 }
186 
187 static int
connect_daemon(struct passwd * pwd,struct sockaddr_un * addr,int * out_sock)188 connect_daemon (struct passwd *pwd,
189                 struct sockaddr_un *addr,
190                 int *out_sock)
191 {
192 	int sock;
193 
194 	/* Now we connect */
195 	sock = socket (AF_UNIX, SOCK_STREAM, 0);
196 	if (sock < 0) {
197 		syslog (GKR_LOG_ERR, "couldn't create control socket: %s", strerror (errno));
198 		return GKD_CONTROL_RESULT_FAILED;
199 	}
200 
201 	/* close on exec */
202 	fcntl (sock, F_SETFD, 1);
203 
204 	if (connect (sock, (struct sockaddr *)addr, sizeof (*addr)) < 0) {
205 		if (errno == ECONNREFUSED) {
206 			close (sock);
207 			return GKD_CONTROL_RESULT_NO_DAEMON;
208 		}
209 		syslog (GKR_LOG_ERR, "couldn't connect to gnome-keyring-daemon socket at: %s: %s",
210 		        addr->sun_path, strerror (errno));
211 		close (sock);
212 		return GKD_CONTROL_RESULT_FAILED;
213 	}
214 
215 	/* Verify the server is running as the right user */
216 
217 	if (check_peer_same_uid (pwd, sock) <= 0) {
218 		close (sock);
219 		return GKD_CONTROL_RESULT_FAILED;
220 	}
221 
222 	/* This lets the server verify us */
223 
224 	if (write_credentials_byte (sock) < 0) {
225 		close (sock);
226 		return GKD_CONTROL_RESULT_FAILED;
227 	}
228 
229 	*out_sock = sock;
230 	return GKD_CONTROL_RESULT_OK;
231 }
232 
233 static void
write_part(int fd,const unsigned char * data,int len,int * res)234 write_part (int fd, const unsigned char *data, int len, int *res)
235 {
236 	assert (res);
237 
238 	/* Already an error present */
239 	if (*res != GKD_CONTROL_RESULT_OK)
240 		return;
241 
242 	assert (data);
243 
244 	while (len > 0) {
245 		int r = write (fd, data, len);
246 		if (r < 0) {
247 			if (errno == EAGAIN)
248 				continue;
249 			syslog (GKR_LOG_ERR, "couldn't send data to gnome-keyring-daemon: %s",
250 			        strerror (errno));
251 			*res = GKD_CONTROL_RESULT_FAILED;
252 			return;
253 		}
254 		data += r;
255 		len -= r;
256 	}
257 }
258 
259 static int
read_part(int fd,unsigned char * data,int len,int disconnect_ok)260 read_part (int fd,
261            unsigned char *data,
262            int len,
263            int disconnect_ok)
264 {
265 	int r, all;
266 
267 	all = len;
268 	while (len > 0) {
269 		r = read (fd, data, len);
270 		if (r < 0) {
271 			if (errno == EAGAIN)
272 				continue;
273 			if (errno == ECONNRESET && disconnect_ok)
274 				return 0;
275 			syslog (GKR_LOG_ERR, "couldn't read data from gnome-keyring-daemon: %s",
276 			        strerror (errno));
277 			return -1;
278 		}
279 		if (r == 0) {
280 			if (disconnect_ok)
281 				return 0;
282 			syslog (GKR_LOG_ERR, "couldn't read data from gnome-keyring-daemon: %s",
283 			        "unexpected end of data");
284 			return -1;
285 		}
286 
287 		data += r;
288 		len -= r;
289 	}
290 
291 	return all;
292 }
293 
294 static int
keyring_daemon_op(struct passwd * pwd,struct sockaddr_un * addr,int op,int argc,const char * argv[])295 keyring_daemon_op (struct passwd *pwd,
296                    struct sockaddr_un *addr,
297                    int op,
298                    int argc,
299                    const char *argv[])
300 {
301 	int ret = GKD_CONTROL_RESULT_OK;
302 	unsigned char buf[4];
303 	int want_disconnect;
304 	int i, sock = -1;
305 	uint oplen, l;
306 
307 	assert (addr);
308 
309 	/*
310 	 * We only support operations with zero or more strings
311 	 * and an empty (only result code) return.
312 	 */
313 
314 	assert (op == GKD_CONTROL_OP_CHANGE ||
315 	        op == GKD_CONTROL_OP_UNLOCK ||
316 	        op == GKD_CONTROL_OP_QUIT);
317 
318 	ret = connect_daemon (pwd, addr, &sock);
319 	if (ret != GKD_CONTROL_RESULT_OK)
320 		goto done;
321 
322 	/* Calculate the packet length */
323 	oplen = 8; /* The packet size, and op code */
324 	for (i = 0; i < argc; ++i)
325 		oplen += 4 + strlen (argv[i]);
326 
327 	/* Write out the length, and op */
328 	egg_buffer_encode_uint32 (buf, oplen);
329 	write_part (sock, buf, 4, &ret);
330 	egg_buffer_encode_uint32 (buf, op);
331 	write_part (sock, buf, 4, &ret);
332 
333 	/* And now the arguments */
334 	for (i = 0; i < argc; ++i) {
335 		if (argv[i] == NULL)
336 			l = 0x7FFFFFFF;
337 		else
338 			l = strlen (argv[i]);
339 		egg_buffer_encode_uint32 (buf, l);
340 		write_part (sock, buf, 4, &ret);
341 		if (argv[i] != NULL)
342 			write_part (sock, (unsigned char*)argv[i], l, &ret);
343 	}
344 
345 	if (ret != GKD_CONTROL_RESULT_OK)
346 		goto done;
347 	/*
348 	 * If we're asking the daemon to quit, then we expect
349 	 * disconnects after we send the initial request
350 	 */
351 	want_disconnect = (op == GKD_CONTROL_OP_QUIT);
352 
353 	/* Read the response length */
354 	if (read_part (sock, buf, 4, want_disconnect) != 4) {
355 		ret = GKD_CONTROL_RESULT_FAILED;
356 		goto done;
357 	}
358 
359 	/* We only support simple responses */
360 	l = egg_buffer_decode_uint32 (buf);
361 	if (l != 8) {
362 		syslog (GKR_LOG_ERR, "invalid length response from gnome-keyring-daemon: %d", l);
363 		ret = GKD_CONTROL_RESULT_FAILED;
364 		goto done;
365 	}
366 
367 	if (read_part (sock, buf, 4, want_disconnect) != 4) {
368 		ret = GKD_CONTROL_RESULT_FAILED;
369 		goto done;
370 	}
371 	ret = egg_buffer_decode_uint32 (buf);
372 
373 	/*
374 	 * If we asked the daemon to quit, wait for it to disconnect
375 	 * by waiting until the socket disconnects from the other end.
376 	 */
377 	if (want_disconnect) {
378 		while (read (sock, buf, 4) > 0);
379 	}
380 
381 done:
382 	if (sock >= 0)
383 		close (sock);
384 
385 	return ret;
386 }
387 
388 int
gkr_pam_client_run_operation(struct passwd * pwd,const char * control,int op,int argc,const char * argv[])389 gkr_pam_client_run_operation (struct passwd *pwd, const char *control,
390                               int op, int argc, const char* argv[])
391 {
392 	struct sigaction ignpipe, oldpipe, defchld, oldchld;
393 	struct sockaddr_un addr;
394 	int res;
395 	pid_t pid;
396 	int status;
397 
398 	/* Make dumb signals go away */
399 	memset (&ignpipe, 0, sizeof (ignpipe));
400 	memset (&oldpipe, 0, sizeof (oldpipe));
401 	ignpipe.sa_handler = SIG_IGN;
402 	sigaction (SIGPIPE, &ignpipe, &oldpipe);
403 
404 	memset (&defchld, 0, sizeof (defchld));
405 	memset (&oldchld, 0, sizeof (oldchld));
406 	defchld.sa_handler = SIG_DFL;
407 	sigaction (SIGCHLD, &defchld, &oldchld);
408 
409 	res = lookup_daemon (pwd, control, &addr);
410 	if (res != GKD_CONTROL_RESULT_OK)
411 		goto out;
412 
413 	if (pwd->pw_uid == getuid () && pwd->pw_gid == getgid () &&
414 	    pwd->pw_uid == geteuid () && pwd->pw_gid == getegid ()) {
415 
416 		/* Already running as the right user, simple */
417 		res = keyring_daemon_op (pwd, &addr, op, argc, argv);
418 
419 	} else {
420 
421 		/* Otherwise run a child process to do the dirty work */
422 		switch (pid = fork ()) {
423 		case -1:
424 			syslog (GKR_LOG_ERR, "gkr-pam: couldn't fork: %s",
425 			        strerror (errno));
426 			res = GKD_CONTROL_RESULT_FAILED;
427 			break;
428 
429 		case 0:
430 			/* Setup process credentials */
431 			if (setgid (pwd->pw_gid) < 0 || setuid (pwd->pw_uid) < 0 ||
432 			    setegid (pwd->pw_gid) < 0 || seteuid (pwd->pw_uid) < 0) {
433 				syslog (GKR_LOG_ERR, "gkr-pam: couldn't switch to user: %s: %s",
434 				        pwd->pw_name, strerror (errno));
435 				exit (GKD_CONTROL_RESULT_FAILED);
436 			}
437 
438 			res = keyring_daemon_op (pwd, &addr, op, argc, argv);
439 			exit (res);
440 			return 0; /* Never reached */
441 
442 		default:
443 			/* wait for child process */
444 			if (wait (&status) != pid) {
445 				syslog (GKR_LOG_ERR, "gkr-pam: couldn't wait on child process: %s",
446 				        strerror (errno));
447 				res = GKD_CONTROL_RESULT_FAILED;
448 			}
449 
450 			res = WEXITSTATUS (status);
451 			break;
452 		};
453 	}
454 
455 out:
456 	sigaction (SIGCHLD, &oldchld, NULL);
457 	sigaction (SIGPIPE, &oldpipe, NULL);
458 
459 	return res;
460 }
461