88c29c5e17ab0859f6b3d7d66cd90e769990b712
[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 "verbose.h"
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 export_format[] = "%x:%x:%x-%s";
53 static char import_format[] = "%x:%x:%x-%n";
54
55 static struct afb_cred *current;
56
57 static struct afb_cred *mkcred(uid_t uid, gid_t gid, pid_t pid, const char *label, size_t size)
58 {
59         struct afb_cred *cred;
60         char *dest, user[64];
61         size_t i;
62         uid_t u;
63
64         i = 0;
65         u = uid;
66         do {
67                 user[i++] = (char)('0' + u % 10);
68                 u = u / 10;
69         } while(u && i < sizeof user);
70
71         cred = malloc(2 + i + size + sizeof *cred);
72         if (!cred)
73                 errno = ENOMEM;
74         else {
75                 cred->refcount = 1;
76                 cred->uid = uid;
77                 cred->gid = gid;
78                 cred->pid = pid;
79                 cred->exported = NULL;
80                 dest = (char*)(&cred[1]);
81                 cred->user = dest;
82                 while(i)
83                         *dest++ = user[--i];
84                 *dest++ = 0;
85                 cred->label = dest;
86                 cred->id = dest;
87                 memcpy(dest, label, size);
88                 dest[size] = 0;
89                 dest = strrchr(dest, ':');
90                 if (dest)
91                         cred->id = &dest[1];
92         }
93         return cred;
94 }
95
96 static struct afb_cred *mkcurrent()
97 {
98         char label[MAX_LABEL_LENGTH];
99         int fd;
100         ssize_t rc;
101
102         fd = open("/proc/self/attr/current", O_RDONLY);
103         if (fd < 0)
104                 rc = 0;
105         else {
106                 rc = read(fd, label, sizeof label);
107                 if (rc < 0)
108                         rc = 0;
109                 close(fd);
110         }
111
112         return mkcred(getuid(), getgid(), getpid(), label, (size_t)rc);
113 }
114
115 struct afb_cred *afb_cred_create(uid_t uid, gid_t gid, pid_t pid, const char *label)
116 {
117         label = label ? : DEFAULT_PEERSEC_LABEL;
118         return mkcred(uid, gid, pid, label, strlen(label));
119 }
120
121 struct afb_cred *afb_cred_create_for_socket(int fd)
122 {
123         int rc;
124         socklen_t length;
125         struct ucred ucred;
126         char label[MAX_LABEL_LENGTH];
127
128         /* get the credentials */
129         length = (socklen_t)(sizeof ucred);
130         rc = getsockopt(fd, SOL_SOCKET, SO_PEERCRED, &ucred, &length);
131         if (rc < 0 || length != (socklen_t)(sizeof ucred) || !~ucred.uid) {
132 #if !defined(NO_DEFAULT_PEERCRED)
133                 ucred.uid = DEFAULT_PEERCRED_UID;
134                 ucred.gid = DEFAULT_PEERCRED_GID;
135                 ucred.pid = DEFAULT_PEERCRED_PID;
136 #else
137                 if (!rc)
138                         errno = EINVAL;
139                 return NULL;
140 #endif
141         }
142
143         /* get the security label */
144         length = (socklen_t)(sizeof label);
145         rc = getsockopt(fd, SOL_SOCKET, SO_PEERSEC, label, &length);
146         if (rc < 0 || length > (socklen_t)(sizeof label)) {
147 #if !defined(NO_DEFAULT_PEERSEC)
148                 length = (socklen_t)strlen(DEFAULT_PEERSEC_LABEL);
149                 strcpy (label, DEFAULT_PEERSEC_LABEL);
150 #else
151                 if (!rc)
152                         errno = EINVAL;
153                 return NULL;
154 #endif
155         }
156
157         /* makes the result */
158         return mkcred(ucred.uid, ucred.gid, ucred.pid, label, (size_t)length);
159 }
160
161 struct afb_cred *afb_cred_addref(struct afb_cred *cred)
162 {
163         if (cred)
164                 __atomic_add_fetch(&cred->refcount, 1, __ATOMIC_RELAXED);
165         return cred;
166 }
167
168 void afb_cred_unref(struct afb_cred *cred)
169 {
170         if (cred && !__atomic_sub_fetch(&cred->refcount, 1, __ATOMIC_RELAXED)) {
171                 if (cred == current)
172                         cred->refcount = 1;
173                 else {
174                         free((void*)cred->exported);
175                         free(cred);
176                 }
177         }
178 }
179
180 struct afb_cred *afb_cred_current()
181 {
182         if (!current)
183                 current = mkcurrent();
184         return afb_cred_addref(current);
185 }
186
187 const char *afb_cred_export(struct afb_cred *cred)
188 {
189         int rc;
190
191         if (!cred->exported) {
192                 rc = asprintf((char**)&cred->exported,
193                         export_format,
194                                 (int)cred->uid,
195                                 (int)cred->gid,
196                                 (int)cred->pid,
197                                 cred->label);
198                 if (rc < 0) {
199                         errno = ENOMEM;
200                         cred->exported = NULL;
201                 }
202         }
203         return cred->exported;
204 }
205
206 struct afb_cred *afb_cred_import(const char *string)
207 {
208         struct afb_cred *cred;
209         int rc, uid, gid, pid, pos;
210
211         rc = sscanf(string, import_format, &uid, &gid, &pid, &pos);
212         if (rc == 3)
213                 cred = afb_cred_create((uid_t)uid, (gid_t)gid, (pid_t)pid, &string[pos]);
214         else {
215                 errno = EINVAL;
216                 cred = NULL;
217         }
218         return cred;
219 }