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