Branch data Line data Source code
1 : : /*
2 : : * Copyright (C) 2008 Stefan Walter
3 : : *
4 : : * Redistribution and use in source and binary forms, with or without
5 : : * modification, are permitted provided that the following conditions
6 : : * are met:
7 : : *
8 : : * * Redistributions of source code must retain the above
9 : : * copyright notice, this list of conditions and the
10 : : * following disclaimer.
11 : : * * Redistributions in binary form must reproduce the
12 : : * above copyright notice, this list of conditions and
13 : : * the following disclaimer in the documentation and/or
14 : : * other materials provided with the distribution.
15 : : * * The names of contributors to this software may not be
16 : : * used to endorse or promote products derived from this
17 : : * software without specific prior written permission.
18 : : *
19 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 : : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 : : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
22 : : * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23 : : * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
24 : : * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
25 : : * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
26 : : * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
27 : : * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28 : : * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
29 : : * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
30 : : * DAMAGE.
31 : : *
32 : : * Author: Stef Walter <stefw@thewalter.net>
33 : : */
34 : :
35 : : #include "config.h"
36 : :
37 : : #include "egg-unix-credentials.h"
38 : :
39 : : #include <sys/types.h>
40 : : #include <sys/socket.h>
41 : : #include <sys/uio.h>
42 : : #include <sys/un.h>
43 : : #include <errno.h>
44 : : #include <stdio.h>
45 : : #include <string.h>
46 : :
47 : : #if defined(HAVE_GETPEERUCRED)
48 : : #include <ucred.h>
49 : : #endif
50 : :
51 : : int
52 : 0 : egg_unix_credentials_read (int sock, pid_t *pid, uid_t *uid)
53 : : {
54 : : struct msghdr msg;
55 : : struct iovec iov;
56 : : char buf;
57 : : int ret;
58 : :
59 : : #if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
60 : : /* Prefer CMSGCRED over LOCAL_CREDS because the former provides the
61 : : * remote PID. */
62 : : #if defined(HAVE_CMSGCRED)
63 : : struct cmsgcred *cred;
64 : : #else /* defined(LOCAL_CREDS) */
65 : : struct sockcred *cred;
66 : : #endif
67 : : union {
68 : : struct cmsghdr hdr;
69 : : char cred[CMSG_SPACE (sizeof *cred)];
70 : : } cmsg;
71 : : #endif
72 : :
73 : 0 : *pid = 0;
74 : 0 : *uid = 0;
75 : :
76 : : /* If LOCAL_CREDS are used in this platform, they have already been
77 : : * initialized by init_connection prior to sending of the credentials
78 : : * byte we receive below. */
79 : :
80 : 0 : iov.iov_base = &buf;
81 : 0 : iov.iov_len = 1;
82 : :
83 : 0 : memset (&msg, 0, sizeof (msg));
84 : 0 : msg.msg_iov = &iov;
85 : 0 : msg.msg_iovlen = 1;
86 : :
87 : : #if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
88 : : memset (&cmsg, 0, sizeof (cmsg));
89 : : msg.msg_control = (caddr_t) &cmsg;
90 : : msg.msg_controllen = CMSG_SPACE(sizeof *cred);
91 : : #endif
92 : :
93 : 0 : again:
94 : 0 : ret = recvmsg (sock, &msg, 0);
95 : :
96 : 0 : if (ret < 0) {
97 : 0 : if (errno == EINTR)
98 : 0 : goto again;
99 : 0 : return -1;
100 : :
101 : 0 : } else if (ret == 0) {
102 : : /* Disconnected */
103 : 0 : return -1;
104 : : }
105 : :
106 : 0 : if (buf != '\0') {
107 : 0 : fprintf (stderr, "credentials byte was not nul\n");
108 : 0 : return -1;
109 : : }
110 : :
111 : : #if defined(HAVE_CMSGCRED) || defined(LOCAL_CREDS)
112 : : if (cmsg.hdr.cmsg_len < CMSG_LEN (sizeof *cred) ||
113 : : cmsg.hdr.cmsg_type != SCM_CREDS) {
114 : : fprintf (stderr, "message from recvmsg() was not SCM_CREDS\n");
115 : : return -1;
116 : : }
117 : : #endif
118 : :
119 : : {
120 : : #ifdef SO_PEERCRED
121 : : #ifndef __OpenBSD__
122 : : struct ucred cr;
123 : : #else
124 : : struct sockpeercred cr;
125 : : #endif
126 : 0 : socklen_t cr_len = sizeof (cr);
127 : :
128 : 0 : if (getsockopt (sock, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
129 : 0 : cr_len == sizeof (cr)) {
130 : 0 : *pid = cr.pid;
131 : 0 : *uid = cr.uid;
132 : : } else {
133 : 0 : fprintf (stderr, "failed to getsockopt() credentials, returned len %d/%d\n",
134 : : cr_len, (int) sizeof (cr));
135 : 0 : return -1;
136 : : }
137 : : #elif defined(HAVE_CMSGCRED)
138 : : cred = (struct cmsgcred *) CMSG_DATA (&cmsg.hdr);
139 : : *pid = cred->cmcred_pid;
140 : : *uid = cred->cmcred_euid;
141 : : #elif defined(LOCAL_CREDS)
142 : : cred = (struct sockcred *) CMSG_DATA (&cmsg.hdr);
143 : : *pid = 0;
144 : : *uid = cred->sc_euid;
145 : : set_local_creds(sock, 0);
146 : : #elif defined(HAVE_GETPEEREID) /* OpenBSD */
147 : : uid_t euid;
148 : : gid_t egid;
149 : : *pid = 0;
150 : :
151 : : if (getpeereid (sock, &euid, &egid) == 0) {
152 : : *uid = euid;
153 : : } else {
154 : : fprintf (stderr, "getpeereid() failed: %s\n", strerror (errno));
155 : : return -1;
156 : : }
157 : : #elif defined(HAVE_GETPEERUCRED)
158 : : ucred_t *uc = NULL;
159 : :
160 : : if (getpeerucred (sock, &uc) == 0) {
161 : : *pid = ucred_getpid (uc);
162 : : *uid = ucred_geteuid (uc);
163 : : ucred_free (uc);
164 : : } else {
165 : : fprintf (stderr, "getpeerucred() failed: %s\n", strerror (errno));
166 : : return -1;
167 : : }
168 : : #else /* !SO_PEERCRED && !HAVE_CMSGCRED */
169 : : fprintf (stderr, "socket credentials not supported on this OS\n");
170 : : return -1;
171 : : #endif
172 : : }
173 : :
174 : 0 : return 0;
175 : : }
176 : :
177 : : int
178 : 0 : egg_unix_credentials_write (int socket)
179 : : {
180 : : char buf;
181 : : int bytes_written;
182 : : #if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
183 : : union {
184 : : struct cmsghdr hdr;
185 : : char cred[CMSG_SPACE (sizeof (struct cmsgcred))];
186 : : } cmsg;
187 : : struct iovec iov;
188 : : struct msghdr msg;
189 : : #endif
190 : :
191 : 0 : buf = 0;
192 : :
193 : : #if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
194 : : iov.iov_base = &buf;
195 : : iov.iov_len = 1;
196 : :
197 : : memset (&msg, 0, sizeof (msg));
198 : : msg.msg_iov = &iov;
199 : : msg.msg_iovlen = 1;
200 : :
201 : : msg.msg_control = (caddr_t) &cmsg;
202 : : msg.msg_controllen = CMSG_SPACE (sizeof (struct cmsgcred));
203 : : memset (&cmsg, 0, sizeof (cmsg));
204 : : cmsg.hdr.cmsg_len = CMSG_LEN (sizeof (struct cmsgcred));
205 : : cmsg.hdr.cmsg_level = SOL_SOCKET;
206 : : cmsg.hdr.cmsg_type = SCM_CREDS;
207 : : #endif
208 : :
209 : 0 : again:
210 : :
211 : : #if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
212 : : bytes_written = sendmsg (socket, &msg, 0);
213 : : #else
214 : 0 : bytes_written = write (socket, &buf, 1);
215 : : #endif
216 : :
217 : 0 : if (bytes_written < 0 && errno == EINTR)
218 : 0 : goto again;
219 : :
220 : 0 : if (bytes_written <= 0)
221 : 0 : return -1;
222 : :
223 : 0 : return 0;
224 : : }
225 : :
226 : : int
227 : 0 : egg_unix_credentials_setup (int sock)
228 : : {
229 : 0 : int retval = 0;
230 : : #if defined(LOCAL_CREDS) && !defined(HAVE_CMSGCRED)
231 : : int val = 1;
232 : : if (setsockopt (sock, 0, LOCAL_CREDS, &val, sizeof (val)) < 0) {
233 : : fprintf (stderr, "unable to set LOCAL_CREDS socket option on fd %d\n", fd);
234 : : retval = -1;
235 : : }
236 : : #endif
237 : 0 : return retval;
238 : : }
239 : :
240 : : char*
241 : 0 : egg_unix_credentials_executable (pid_t pid)
242 : : {
243 : 0 : char *result = NULL;
244 : :
245 : : /* Try and figure out the path from the pid */
246 : : #if defined(__linux__) || defined(__FreeBSD__)
247 : : char path[1024];
248 : : char buffer[64];
249 : : int count;
250 : :
251 : : #if defined(__linux__)
252 : 0 : snprintf (buffer, sizeof (buffer), "/proc/%d/exe", (int)pid);
253 : : #elif defined(__FreeBSD__)
254 : : snprintf (buffer, sizeof (buffer), "/proc/%d/file", (int)pid);
255 : : #endif
256 : :
257 : 0 : count = readlink (buffer, path, sizeof (path));
258 : 0 : if (count < 0)
259 : 0 : fprintf (stderr, "readlink failed for file: %s", buffer);
260 : : else
261 : 0 : result = strndup (path, count);
262 : : #endif
263 : :
264 : 0 : return result;
265 : : }
|