Use afb_token in contexts
[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
36 #define MAX_LABEL_LENGTH  1024
37
38 #if !defined(NO_DEFAULT_PEERCRED) && !defined(ADD_DEFAULT_PEERCRED)
39 #  define NO_DEFAULT_PEERCRED
40 #endif
41
42 #if !defined(DEFAULT_PEERSEC_LABEL)
43 #  define DEFAULT_PEERSEC_LABEL "NoLabel"
44 #endif
45 #if !defined(DEFAULT_PEERCRED_UID)
46 #  define DEFAULT_PEERCRED_UID 99 /* nobody */
47 #endif
48 #if !defined(DEFAULT_PEERCRED_GID)
49 #  define DEFAULT_PEERCRED_GID 99 /* nobody */
50 #endif
51 #if !defined(DEFAULT_PEERCRED_PID)
52 #  define DEFAULT_PEERCRED_PID 0  /* no process */
53 #endif
54
55 static char on_behalf_credential_permission[] = "urn:AGL:permission:*:partner:on-behalf-credentials";
56 static char export_format[] = "%x:%x:%x-%s";
57 static char import_format[] = "%x:%x:%x-%n";
58
59 static struct afb_cred *current;
60
61 static struct afb_cred *mkcred(uid_t uid, gid_t gid, pid_t pid, const char *label, size_t size)
62 {
63         struct afb_cred *cred;
64         char *dest, user[64];
65         size_t i;
66         uid_t u;
67
68         i = 0;
69         u = uid;
70         do {
71                 user[i++] = (char)('0' + u % 10);
72                 u = u / 10;
73         } while(u && i < sizeof user);
74
75         cred = malloc(2 + i + size + sizeof *cred);
76         if (!cred)
77                 errno = ENOMEM;
78         else {
79                 cred->refcount = 1;
80                 cred->uid = uid;
81                 cred->gid = gid;
82                 cred->pid = pid;
83                 cred->exported = NULL;
84                 dest = (char*)(&cred[1]);
85                 cred->user = dest;
86                 while(i)
87                         *dest++ = user[--i];
88                 *dest++ = 0;
89                 cred->label = dest;
90                 cred->id = dest;
91                 memcpy(dest, label, size);
92                 dest[size] = 0;
93                 dest = strrchr(dest, ':');
94                 if (dest)
95                         cred->id = &dest[1];
96         }
97         return cred;
98 }
99
100 static struct afb_cred *mkcurrent()
101 {
102         char label[MAX_LABEL_LENGTH];
103         int fd;
104         ssize_t rc;
105
106         fd = open("/proc/self/attr/current", O_RDONLY);
107         if (fd < 0)
108                 rc = 0;
109         else {
110                 rc = read(fd, label, sizeof label);
111                 if (rc < 0)
112                         rc = 0;
113                 close(fd);
114         }
115
116         return mkcred(getuid(), getgid(), getpid(), label, (size_t)rc);
117 }
118
119 struct afb_cred *afb_cred_create(uid_t uid, gid_t gid, pid_t pid, const char *label)
120 {
121         label = label ? : DEFAULT_PEERSEC_LABEL;
122         return mkcred(uid, gid, pid, label, strlen(label));
123 }
124
125 struct afb_cred *afb_cred_create_for_socket(int fd)
126 {
127         int rc;
128         socklen_t length;
129         struct ucred ucred;
130         char label[MAX_LABEL_LENGTH];
131
132         /* get the credentials */
133         length = (socklen_t)(sizeof ucred);
134         rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length);
135         if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid) {
136 #if !defined(NO_DEFAULT_PEERCRED)
137                 ucred.uid = DEFAULT_PEERCRED_UID;
138                 ucred.gid = DEFAULT_PEERCRED_GID;
139                 ucred.pid = DEFAULT_PEERCRED_PID;
140 #else
141                 if (!rc)
142                         errno = EINVAL;
143                 return NULL;
144 #endif
145         }
146
147         /* get the security label */
148         length = (socklen_t)(sizeof label);
149         rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, label, &length);
150         if (rc < 0 || length > (socklen_t)(sizeof label)) {
151 #if !defined(NO_DEFAULT_PEERSEC)
152                 length = (socklen_t)strlen(DEFAULT_PEERSEC_LABEL);
153                 strcpy (label, DEFAULT_PEERSEC_LABEL);
154 #else
155                 if (!rc)
156                         errno = EINVAL;
157                 return NULL;
158 #endif
159         }
160
161         /* makes the result */
162         return mkcred(ucred.uid, ucred.gid, ucred.pid, label, (size_t)length);
163 }
164
165 struct afb_cred *afb_cred_addref(struct afb_cred *cred)
166 {
167         if (cred)
168                 __atomic_add_fetch(&cred->refcount, 1, __ATOMIC_RELAXED);
169         return cred;
170 }
171
172 void afb_cred_unref(struct afb_cred *cred)
173 {
174         if (cred && !__atomic_sub_fetch(&cred->refcount, 1, __ATOMIC_RELAXED)) {
175                 if (cred == current)
176                         cred->refcount = 1;
177                 else {
178                         free((void*)cred->exported);
179                         free(cred);
180                 }
181         }
182 }
183
184 struct afb_cred *afb_cred_current()
185 {
186         if (!current)
187                 current = mkcurrent();
188         return afb_cred_addref(current);
189 }
190
191 const char *afb_cred_export(struct afb_cred *cred)
192 {
193         int rc;
194
195         if (!cred->exported) {
196                 rc = asprintf((char**)&cred->exported,
197                         export_format,
198                                 (int)cred->uid,
199                                 (int)cred->gid,
200                                 (int)cred->pid,
201                                 cred->label);
202                 if (rc < 0) {
203                         errno = ENOMEM;
204                         cred->exported = NULL;
205                 }
206         }
207         return cred->exported;
208 }
209
210 struct afb_cred *afb_cred_import(const char *string)
211 {
212         struct afb_cred *cred;
213         int rc, uid, gid, pid, pos;
214
215         rc = sscanf(string, import_format, &uid, &gid, &pid, &pos);
216         if (rc == 3)
217                 cred = afb_cred_create((uid_t)uid, (gid_t)gid, (pid_t)pid, &string[pos]);
218         else {
219                 errno = EINVAL;
220                 cred = NULL;
221         }
222         return cred;
223 }
224
225 struct afb_cred *afb_cred_mixed_on_behalf_import(struct afb_cred *cred, struct afb_context *context, const char *exported)
226
227 {
228         struct afb_cred *imported;
229         if (exported) {
230                 if (afb_cred_has_permission(cred, on_behalf_credential_permission, context)) {
231                         imported = afb_cred_import(exported);
232                         if (imported)
233                                 return imported;
234                         ERROR("Can't import on behalf credentials: %m");
235                 } else {
236                         ERROR("On behalf credentials refused");
237                 }
238         }
239         return afb_cred_addref(cred);
240 }
241
242 /*********************************************************************************/
243 static const char *token_of_context(struct afb_context *context)
244 {
245         return context && context->token ? afb_token_string(context->token) : "X";
246 }
247
248 /*********************************************************************************/
249 #ifdef BACKEND_PERMISSION_IS_CYNARA
250
251 #include <pthread.h>
252 #include <cynara-client.h>
253
254 static cynara *handle;
255 static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
256
257 int afb_cred_has_permission(struct afb_cred *cred, const char *permission, struct afb_context *context)
258 {
259         int rc;
260
261         if (!cred) {
262                 /* case of permission for self */
263                 return 1;
264         }
265         if (!permission) {
266                 ERROR("Got a null permission!");
267                 return 0;
268         }
269
270         /* cynara isn't reentrant */
271         pthread_mutex_lock(&mutex);
272
273         /* lazy initialisation */
274         if (!handle) {
275                 rc = cynara_initialize(&handle, NULL);
276                 if (rc != CYNARA_API_SUCCESS) {
277                         handle = NULL;
278                         ERROR("cynara initialisation failed with code %d", rc);
279                         return 0;
280                 }
281         }
282
283         /* query cynara permission */
284         rc = cynara_check(handle, cred->label, token_of_context(context), cred->user, permission);
285
286         pthread_mutex_unlock(&mutex);
287         return rc == CYNARA_API_ACCESS_ALLOWED;
288 }
289
290 /*********************************************************************************/
291 #else
292 int afb_cred_has_permission(struct afb_cred *cred, const char *permission, struct afb_context *context)
293 {
294         WARNING("Granting permission %s by default of backend", permission ?: "(null)");
295         return !!permission;
296 }
297 #endif
298