78e9733188b7dad1c467022b7cc2c935dcc5756a
[apps/agl-service-data-persistence.git] / ll-auth-binding / src / ll-auth-binding.c
1 #define _GNU_SOURCE
2 #include <stdio.h>
3 #include <string.h>
4 #include <json-c/json.h>
5 #include <security/pam_appl.h>
6 #include <security/pam_misc.h>
7
8 #define AFB_BINDING_VERSION 2
9 #include <afb/afb-binding.h>
10
11 static struct pam_conv conv = { misc_conv, NULL };
12
13 static char* current_device = NULL;
14 static char* current_user = NULL;
15
16 afb_event evt_login, evt_logout, evt_failed;
17
18 /// @brief API's verb 'login'. Try to login a user using a device
19 /// @param[in] req The request object. Should contains a json with a "device" key.
20 static void verb_login(struct afb_req req)
21 {
22         struct json_object* args = NULL;
23         struct json_object* device_object = NULL;
24         pam_handle_t* pamh;
25         int r;
26
27         if (current_user)
28         {
29                 AFB_ERROR("[login] the current user must be logged out first!");
30                 afb_req_fail(req, "current user must be logged out first!", NULL);
31                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"A user is already logged in!\"}"));
32                 return;
33         }
34
35         args = afb_req_json(req);
36         if (args == NULL || !json_object_object_get_ex(args, "device", &device_object))
37         {
38                 AFB_ERROR("[login] device must be provided!");
39                 afb_req_fail(req, "device must be provided!", NULL);
40                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"device must be provided!\"}"));
41                 return;
42         }
43
44         const char* device = json_object_get_string(device_object);
45
46         if ((r = pam_start("agl", NULL, &conv, &pamh)) != PAM_SUCCESS)
47         {
48                 AFB_ERROR("PAM start failed!");
49                 afb_req_fail(req, "PAM start failed!", NULL);
50                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"PAM start failed!\"}"));
51                 return;
52         }
53
54         char pam_variable[4096] = "DEVICE=";
55         strcat(pam_variable, device);
56         
57         if ((r = pam_putenv(pamh, pam_variable)) != PAM_SUCCESS)
58         {
59                 AFB_ERROR("PAM putenv failed!");
60                 afb_req_fail(req, "PAM putenv failed!", NULL);
61                 pam_end(pamh, r);
62                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"PAM putenv failed!\"}"));
63                 return;
64         }
65
66         if ((r = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
67         {
68                 AFB_ERROR("PAM authenticate failed!");
69                 afb_req_fail(req, "PAM authenticate failed!", NULL);
70                 pam_end(pamh, r);
71                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"PAM authenticate failed!\"}"));
72                 return;
73         }
74
75         if ((r = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS)
76         {
77                 AFB_ERROR("PAM acct_mgmt failed!");
78                 afb_req_fail(req, "PAM acct_mgmt failed!", NULL);
79                 pam_end(pamh, r);
80                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"PAM acct_mgmt failed!\"}"));
81                 return;
82         }
83
84         const char* pam_user;
85         pam_get_item(pamh, PAM_USER, (const void**)&pam_user);
86         if (!pam_user)
87         {
88                 AFB_ERROR("[login] No user provided by the PAM module!");
89                 afb_req_fail(req, "No user provided by the PAM module!", NULL);
90                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"No user provided by the PAM module!\"}"));
91                 return;
92         }
93
94         current_device = strdup(device);
95         current_user = strdup(pam_user);
96
97         if ((r = pam_end(pamh, r)) != PAM_SUCCESS)
98         {
99                 AFB_ERROR("PAM end failed!");
100                 afb_req_fail(req, "PAM end failed!", NULL);
101                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"PAM end failed!\"}"));
102                 return;
103         }
104
105         AFB_INFO("[login] device: %s, user: %s", current_device, current_user);
106         json_object* result = json_object_new_object();
107         json_object_object_add(result, "device", json_object_new_string(current_device));
108         json_object_object_add(result, "user", json_object_new_string(current_user));
109         afb_req_success(req, NULL, current_device);
110         afb_event_broadcast(evt_login, result);
111 }
112
113 /// @brief API's verb 'lgout'. Try to logout a user using a device
114 /// @param[in] req The request object. Should contains a json with a "device" key.
115 static void verb_logout(struct afb_req req)
116 {
117         struct json_object* args = NULL;
118         struct json_object* device_object = NULL;
119
120         args = afb_req_json(req);
121         if (args == NULL || !json_object_object_get_ex(args, "device", &device_object))
122         {
123                 AFB_INFO("[logout] device must be provided!");
124                 afb_req_fail(req, "device must be provided!", NULL);
125                 afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"Device must be provided!\"}"));
126                 return;
127         }
128
129         const char* device = json_object_get_string(device_object);
130         if (current_device && !strcmp(device, current_device))
131         {
132                 free(current_device);
133                 current_device = NULL;
134                 if (current_user)
135                 {
136                         free(current_user);
137                         current_user = NULL;
138                         AFB_INFO("[logout] device: %s", device);
139                         afb_req_success(req, NULL, device);
140                         afb_event_broadcast(evt_logout, NULL);
141                         return;
142                 }
143                 else
144                 {
145                         AFB_INFO("No user was linked to this device!");
146                         afb_req_fail(req, "No user was linked to this device!", NULL);
147                         afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"No user was linked to this device!\"}"));
148                         return;
149                 }
150         }
151
152         AFB_INFO("The unplugged device wasn't the user key!");
153         afb_req_fail(req, "The unplugged device wasn't the user key!", NULL);
154         afb_event_broadcast(evt_failed, json_tokener_parse("{\"message\":\"The unplugged device wasn't the user key!\"}"));
155 }
156
157 static void verb_getuser(struct afb_req req)
158 {
159         if (!current_device || !current_user)
160         {
161                 afb_req_fail(req, "there is no logged user!", NULL);
162                 return;
163         }
164
165         json_object* result = json_object_new_object();
166         json_object_object_add(result, "user", json_object_new_string(current_user));
167         json_object_object_add(result, "device", json_object_new_string(current_device));
168
169         afb_req_success(req, result, NULL);
170 }
171
172 int ll_auth_init()
173 {
174         current_user = NULL;
175         current_device = NULL;
176         evt_login = afb_daemon_make_event("login");
177         evt_logout = afb_daemon_make_event("logout");
178         evt_failed = afb_daemon_make_event("failed");
179
180         if (afb_event_is_valid(evt_login) && afb_event_is_valid(evt_logout))
181                 return 0;
182
183         AFB_ERROR("Can't create events");
184                 return -1;
185 }
186
187 static const afb_verb_v2 _ll_auth_binding_verbs[]= {
188         {
189                 .verb = "login",
190                 .callback = verb_login,
191                 .auth = NULL,
192                 .info = NULL,
193                 .session = AFB_SESSION_NONE_V2
194         },
195         {
196                 .verb = "logout",
197                 .callback = verb_logout,
198                 .auth = NULL,
199                 .info = NULL,
200                 .session = AFB_SESSION_NONE_V2
201         },
202         {
203                 .verb = "getuser",
204                 .callback = verb_getuser,
205                 .auth = NULL,
206                 .info = NULL,
207                 .session = AFB_SESSION_NONE_V2
208         },
209         { .verb=NULL}
210 };
211
212 const struct afb_binding_v2 afbBindingV2 = {
213                 .api = "ll-auth",
214                 .specification = NULL,
215                 .verbs = _ll_auth_binding_verbs,
216                 .preinit = ll_auth_init,
217                 .init = NULL,
218                 .onevent = NULL,
219                 .noconcurrency = 0
220 };