afb-context: Move credentials to context
[src/app-framework-binder.git] / src / afb-cred.c
1 /*
2  * Copyright (C) 2017-2019 "IoT.bzh"
3  * Author: José Bollo <jose.bollo@iot.bzh>
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *   http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17
18 #define _GNU_SOURCE
19
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <stdint.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <fcntl.h>
27 #include <sys/types.h>
28 #include <sys/socket.h>
29
30 #include "afb-cred.h"
31 #include "afb-context.h"
32 #include "afb-token.h"
33 #include "verbose.h"
34
35 #define MAX_LABEL_LENGTH  1024
36
37 #if !defined(NO_DEFAULT_PEERCRED) && !defined(ADD_DEFAULT_PEERCRED)
38 #  define NO_DEFAULT_PEERCRED
39 #endif
40
41 #if !defined(DEFAULT_PEERSEC_LABEL)
42 #  define DEFAULT_PEERSEC_LABEL "NoLabel"
43 #endif
44 #if !defined(DEFAULT_PEERCRED_UID)
45 #  define DEFAULT_PEERCRED_UID 99 /* nobody */
46 #endif
47 #if !defined(DEFAULT_PEERCRED_GID)
48 #  define DEFAULT_PEERCRED_GID 99 /* nobody */
49 #endif
50 #if !defined(DEFAULT_PEERCRED_PID)
51 #  define DEFAULT_PEERCRED_PID 0  /* no process */
52 #endif
53
54 static char export_format[] = "%x:%x:%x-%s";
55 static char import_format[] = "%x:%x:%x-%n";
56
57 static struct afb_cred *current;
58
59 static struct afb_cred *mkcred(uid_t uid, gid_t gid, pid_t pid, const char *label, size_t size)
60 {
61         struct afb_cred *cred;
62         char *dest, user[64];
63         size_t i;
64         uid_t u;
65
66         i = 0;
67         u = uid;
68         do {
69                 user[i++] = (char)('0' + u % 10);
70                 u = u / 10;
71         } while(u && i < sizeof user);
72
73         cred = malloc(2 + i + size + sizeof *cred);
74         if (!cred)
75                 errno = ENOMEM;
76         else {
77                 cred->refcount = 1;
78                 cred->uid = uid;
79                 cred->gid = gid;
80                 cred->pid = pid;
81                 cred->exported = NULL;
82                 dest = (char*)(&cred[1]);
83                 cred->user = dest;
84                 while(i)
85                         *dest++ = user[--i];
86                 *dest++ = 0;
87                 cred->label = dest;
88                 cred->id = dest;
89                 memcpy(dest, label, size);
90                 dest[size] = 0;
91                 dest = strrchr(dest, ':');
92                 if (dest)
93                         cred->id = &dest[1];
94         }
95         return cred;
96 }
97
98 static struct afb_cred *mkcurrent()
99 {
100         char label[MAX_LABEL_LENGTH];
101         int fd;
102         ssize_t rc;
103
104         fd = open("/proc/self/attr/current", O_RDONLY);
105         if (fd < 0)
106                 rc = 0;
107         else {
108                 rc = read(fd, label, sizeof label);
109                 if (rc < 0)
110                         rc = 0;
111                 close(fd);
112         }
113
114         return mkcred(getuid(), getgid(), getpid(), label, (size_t)rc);
115 }
116
117 struct afb_cred *afb_cred_create(uid_t uid, gid_t gid, pid_t pid, const char *label)
118 {
119         label = label ? : DEFAULT_PEERSEC_LABEL;
120         return mkcred(uid, gid, pid, label, strlen(label));
121 }
122
123 struct afb_cred *afb_cred_create_for_socket(int fd)
124 {
125         int rc;
126         socklen_t length;
127         struct ucred ucred;
128         char label[MAX_LABEL_LENGTH];
129
130         /* get the credentials */
131         length = (socklen_t)(sizeof ucred);
132         rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length);
133         if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid) {
134 #if !defined(NO_DEFAULT_PEERCRED)
135                 ucred.uid = DEFAULT_PEERCRED_UID;
136                 ucred.gid = DEFAULT_PEERCRED_GID;
137                 ucred.pid = DEFAULT_PEERCRED_PID;
138 #else
139                 if (!rc)
140                         errno = EINVAL;
141                 return NULL;
142 #endif
143         }
144
145         /* get the security label */
146         length = (socklen_t)(sizeof label);
147         rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, label, &length);
148         if (rc < 0 || length > (socklen_t)(sizeof label)) {
149 #if !defined(NO_DEFAULT_PEERSEC)
150                 length = (socklen_t)strlen(DEFAULT_PEERSEC_LABEL);
151                 strcpy (label, DEFAULT_PEERSEC_LABEL);
152 #else
153                 if (!rc)
154                         errno = EINVAL;
155                 return NULL;
156 #endif
157         }
158
159         /* makes the result */
160         return mkcred(ucred.uid, ucred.gid, ucred.pid, label, (size_t)length);
161 }
162
163 struct afb_cred *afb_cred_addref(struct afb_cred *cred)
164 {
165         if (cred)
166                 __atomic_add_fetch(&cred->refcount, 1, __ATOMIC_RELAXED);
167         return cred;
168 }
169
170 void afb_cred_unref(struct afb_cred *cred)
171 {
172         if (cred && !__atomic_sub_fetch(&cred->refcount, 1, __ATOMIC_RELAXED)) {
173                 if (cred == current)
174                         cred->refcount = 1;
175                 else {
176                         free((void*)cred->exported);
177                         free(cred);
178                 }
179         }
180 }
181
182 struct afb_cred *afb_cred_current()
183 {
184         if (!current)
185                 current = mkcurrent();
186         return afb_cred_addref(current);
187 }
188
189 const char *afb_cred_export(struct afb_cred *cred)
190 {
191         int rc;
192
193         if (!cred->exported) {
194                 rc = asprintf((char**)&cred->exported,
195                         export_format,
196                                 (int)cred->uid,
197                                 (int)cred->gid,
198                                 (int)cred->pid,
199                                 cred->label);
200                 if (rc < 0) {
201                         errno = ENOMEM;
202                         cred->exported = NULL;
203                 }
204         }
205         return cred->exported;
206 }
207
208 struct afb_cred *afb_cred_import(const char *string)
209 {
210         struct afb_cred *cred;
211         int rc, uid, gid, pid, pos;
212
213         rc = sscanf(string, import_format, &uid, &gid, &pid, &pos);
214         if (rc == 3)
215                 cred = afb_cred_create((uid_t)uid, (gid_t)gid, (pid_t)pid, &string[pos]);
216         else {
217                 errno = EINVAL;
218                 cred = NULL;
219         }
220         return cred;
221 }
222
223 /*********************************************************************************/
224 static const char *token_of_context(struct afb_context *context)
225 {
226         return context && context->token ? afb_token_string(context->token) : "X";
227 }
228
229 /*********************************************************************************/
230 #ifdef BACKEND_PERMISSION_IS_CYNARA
231
232 #include <pthread.h>
233 #include <cynara-client.h>
234
235 static cynara *handle;
236 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
237
238 int afb_cred_has_permission(struct afb_cred *cred, const char *permission, struct afb_context *context)
239 {
240         int rc;
241
242         if (!cred) {
243                 /* case of permission for self */
244                 return 1;
245         }
246         if (!permission) {
247                 ERROR("Got a null permission!");
248                 return 0;
249         }
250
251         /* cynara isn't reentrant */
252         pthread_mutex_lock(&mutex);
253
254         /* lazy initialisation */
255         if (!handle) {
256                 rc = cynara_initialize(&handle, NULL);
257                 if (rc != CYNARA_API_SUCCESS) {
258                         handle = NULL;
259                         ERROR("cynara initialisation failed with code %d", rc);
260                         return 0;
261                 }
262         }
263
264         /* query cynara permission */
265         rc = cynara_check(handle, cred->label, token_of_context(context), cred->user, permission);
266
267         pthread_mutex_unlock(&mutex);
268         return rc == CYNARA_API_ACCESS_ALLOWED;
269 }
270
271 /*********************************************************************************/
272 #else
273 int afb_cred_has_permission(struct afb_cred *cred, const char *permission, struct afb_context *context)
274 {
275         WARNING("Granting permission %s by default of backend", permission ?: "(null)");
276         return !!permission;
277 }
278 #endif
279