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 7 : 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 7 : *pid = 0;
74 7 : *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 7 : iov.iov_base = &buf;
81 7 : iov.iov_len = 1;
82 :
83 7 : memset (&msg, 0, sizeof (msg));
84 7 : msg.msg_iov = &iov;
85 7 : 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 7 : again:
94 7 : ret = recvmsg (sock, &msg, 0);
95 :
96 7 : if (ret < 0) {
97 0 : if (errno == EINTR)
98 0 : goto again;
99 0 : return -1;
100 :
101 7 : } else if (ret == 0) {
102 : /* Disconnected */
103 0 : return -1;
104 : }
105 :
106 7 : 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 7 : socklen_t cr_len = sizeof (cr);
127 :
128 7 : if (getsockopt (sock, SOL_SOCKET, SO_PEERCRED, &cr, &cr_len) == 0 &&
129 7 : cr_len == sizeof (cr)) {
130 7 : *pid = cr.pid;
131 7 : *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 7 : return 0;
175 : }
176 :
177 : int
178 7 : 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 7 : 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 7 : again:
210 :
211 : #if defined(HAVE_CMSGCRED) && (!defined(LOCAL_CREDS) || defined(__FreeBSD__))
212 : bytes_written = sendmsg (socket, &msg, 0);
213 : #else
214 7 : bytes_written = write (socket, &buf, 1);
215 : #endif
216 :
217 7 : if (bytes_written < 0 && errno == EINTR)
218 0 : goto again;
219 :
220 7 : if (bytes_written <= 0)
221 0 : return -1;
222 :
223 7 : return 0;
224 : }
225 :
226 : int
227 27 : egg_unix_credentials_setup (int sock)
228 : {
229 27 : 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 27 : 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 : }
|