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