4 #include <json-c/json.h>
5 #include <security/pam_appl.h>
6 #include <security/pam_misc.h>
11 #define AFB_BINDING_VERSION 2
12 #include <afb/afb-binding.h>
15 #define PAM_RULE "agl"
16 #define UDEV_MONITOR_POLLING_TIMEOUT 5000
18 #define UDEV_ACTION_UNSUPPORTED 0
19 #define UDEV_ACTION_ADD 1
20 #define UDEV_ACTION_REMOVE 2
22 #define LOGIN_SUCCESS 0
23 #define LOGIN_ERROR_USER_LOGGED 1
24 #define LOGIN_ERROR_PAM_START 2
25 #define LOGIN_ERROR_PAM_PUTENV 3
26 #define LOGIN_ERROR_PAM_AUTHENTICATE 4
27 #define LOGIN_ERROR_PAM_ACCT_MGMT 5
28 #define LOGIN_ERROR_PAM_NO_USER 6
29 #define LOGIN_ERROR_PAM_END 7
32 static const char* error_messages[] =
35 "The current user must be logged out first!",
38 "PAM authenticate failed!",
39 "PAM acct_mgmt failed!",
40 "No user provided by the PAM module!",
44 static char* current_device = NULL;
45 static char* current_user = NULL;
46 static struct pam_conv conv = { misc_conv, NULL };
47 static struct udev* udev_context = NULL;
48 static struct udev_monitor* udev_mon = NULL;
49 static pthread_t udev_monitoring_thread_handle;
50 static struct afb_event evt_login, evt_logout, evt_failed;
53 * @brief Free the memory associated to the specified string and nullify the pointer.
54 * @param[in] string A pointer to the string.
56 static inline void free_string(char** string)
60 if (*string) free(*string);
66 * @brief Free the memory associated to the specified UDev's context and nullify the pointer.
67 * @param[in] ctx UDev's context.
69 static inline void free_udev_context(struct udev** ctx)
73 if (*ctx) udev_unref(*ctx);
79 * @brief Free the memory associated to the specified UDev's monitor and nullify the pointer.
80 * @param[in] mon UDev's monitor.
82 static inline void free_udev_monitor(struct udev_monitor** mon)
86 if (*mon) udev_monitor_unref(*mon);
92 * @brief Print UDev infos for the specified device.
93 * @param[in] dev The device.
95 static inline void print_udev_device_info(struct udev_device* dev)
97 AFB_INFO(" Action: %s", udev_device_get_action(dev));
98 AFB_INFO(" Node: %s", udev_device_get_devnode(dev));
99 AFB_INFO(" Subsystem: %s", udev_device_get_subsystem(dev));
100 AFB_INFO(" Devtype: %s", udev_device_get_devtype(dev));
101 AFB_INFO(" DevNum: %lu", udev_device_get_devnum(dev));
102 AFB_INFO(" DevPath: %s", udev_device_get_devpath(dev));
103 AFB_INFO(" Driver: %s", udev_device_get_driver(dev));
104 AFB_INFO(" SeqNum: %llu", udev_device_get_seqnum(dev));
105 AFB_INFO(" SysName: %s", udev_device_get_sysname(dev));
106 AFB_INFO(" SysNum: %s", udev_device_get_sysnum(dev));
107 AFB_INFO(" SysPath: %s", udev_device_get_syspath(dev));
111 * @brief Get the UDev's action as an int to allow switch condition.
112 * @param[in] dev The device.
114 static inline int udev_device_get_action_int(struct udev_device* dev)
116 const char* action = udev_device_get_action(dev);
118 strcmp(action, "add")
119 ? (strcmp(action, "remove") ? UDEV_ACTION_UNSUPPORTED : UDEV_ACTION_REMOVE)
124 * @brief PAM authentication process.
125 * @param[in] pamh The handle to the PAM context.
126 * @param[in] device The device to login.
128 static int pam_process(pam_handle_t* pamh, const char* device)
132 if (!pamh) return LOGIN_ERROR_PAM_START;
134 char pam_variable[4096] = "DEVICE=";
135 strcat(pam_variable, device);
137 if ((r = pam_putenv(pamh, pam_variable)) != PAM_SUCCESS)
138 return LOGIN_ERROR_PAM_PUTENV;
140 if ((r = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
141 return LOGIN_ERROR_PAM_AUTHENTICATE;
143 if ((r = pam_acct_mgmt(pamh, 0)) != PAM_SUCCESS)
144 return LOGIN_ERROR_PAM_ACCT_MGMT;
146 const char* pam_user;
147 pam_get_item(pamh, PAM_USER, (const void**)&pam_user);
149 return LOGIN_ERROR_PAM_NO_USER;
151 current_device = strdup(device);
152 current_user = strdup(pam_user);
154 return LOGIN_SUCCESS;
158 * @brief Login using PAM.
159 * @param[in] device The device to use.
160 * @return Exit code, @c LOGIN_SUCCESS on success.
162 static int login_pam(const char* device)
168 return LOGIN_ERROR_USER_LOGGED;
170 if ((r = pam_start("agl", NULL, &conv, &pamh)) != PAM_SUCCESS)
171 return LOGIN_ERROR_PAM_START;
173 r = pam_process(pamh, device);
174 if (r != LOGIN_SUCCESS)
180 if ((r = pam_end(pamh, r)) != PAM_SUCCESS)
181 return LOGIN_ERROR_PAM_END;
183 return LOGIN_SUCCESS;
187 * @brief Try to login a user using a device.
188 * @param[in] device The device to use.
189 * @return Exit code, @c LOGIN_SUCCESS if success.
191 static int login(const char* device)
194 struct json_object* result;
196 result = json_object_new_object();
198 ret = login_pam(device);
202 json_object_object_add(result, "device", json_object_new_string(current_device));
203 json_object_object_add(result, "user", json_object_new_string(current_user));
204 afb_event_broadcast(evt_login, result);
207 json_object_object_add(result, "message", json_object_new_string(error_messages[ret]));
208 afb_event_broadcast(evt_failed, result);
211 json_object_put(result);
215 /// @brief Try to logout a user using a device.
216 /// @param[in] device The device to logout.
217 static void logout(const char* device)
219 struct json_object* result;
221 result = json_object_new_object();
223 if (current_device && !strcmp(device, current_device))
225 json_object_object_add(result, "device", json_object_new_string(current_device));
226 json_object_object_add(result, "user", json_object_new_string(current_user));
227 AFB_INFO("[logout] device: %s", device);
228 afb_event_broadcast(evt_logout, NULL);
230 free_string(¤t_device);
231 free_string(¤t_user);
235 json_object_object_add(result, "message", json_object_new_string("The unplugged device wasn't the user key!"));
236 AFB_INFO("The unplugged device wasn't the user key!");
237 afb_event_broadcast(evt_failed, result);
239 json_object_put(result);
243 * @brief UDev's monitoring thread.
245 void* udev_monitoring_thread(void* arg)
247 struct udev_device* dev;
251 pfd.fd = udev_monitor_get_fd(udev_mon);
256 if (poll(&pfd, 1, UDEV_MONITOR_POLLING_TIMEOUT))
258 dev = udev_monitor_receive_device(udev_mon);
261 if (!strcmp(udev_device_get_devtype(dev), "disk"))
263 action = udev_device_get_action_int(dev);
266 case UDEV_ACTION_ADD:
267 AFB_INFO("A device is plugged-in");
268 print_udev_device_info(dev);
269 login(udev_device_get_devnode(dev));
271 case UDEV_ACTION_REMOVE:
272 AFB_INFO("A device is plugged-out");
273 print_udev_device_info(dev);
274 logout(udev_device_get_devnode(dev));
277 AFB_DEBUG("Unsupported udev action");
281 udev_device_unref(dev);
285 AFB_ERROR("No Device from udev_monitor_receive_device().");
290 AFB_DEBUG("Udev polling timeout");
297 * @brief API's verb 'getuser'. Try to get user informations.
298 * @param[in] req The request object.
300 static void verb_getuser(struct afb_req req)
302 if (!current_device || !current_user)
304 afb_req_fail(req, "there is no logged user!", NULL);
308 json_object* result = json_object_new_object();
309 json_object_object_add(result, "user", json_object_new_string(current_user));
310 json_object_object_add(result, "device", json_object_new_string(current_device));
312 afb_req_success(req, result, NULL);
316 * @brief Do the cleanup when init fails.
317 * @param[in] error Error message.
318 * @param[in] retcode Error code to return.
319 * @return An exit code equals to @c retcode.
321 static inline int ll_auth_init_cleanup(const char* error, int retcode)
323 AFB_ERROR_V2("%s", error);
324 free_string(¤t_user);
325 free_string(¤t_device);
327 free_udev_monitor(&udev_mon);
328 free_udev_context(&udev_context);
333 * @brief Initialize the binding.
337 evt_login = afb_daemon_make_event("login");
338 evt_logout = afb_daemon_make_event("logout");
339 evt_failed = afb_daemon_make_event("failed");
341 if (!afb_event_is_valid(evt_login) || !afb_event_is_valid(evt_logout) || !afb_event_is_valid(evt_failed))
342 return ll_auth_init_cleanup("Can't create events", -1);
344 udev_context = udev_new();
346 return ll_auth_init_cleanup("Can't initialize udev's context", -1);
348 udev_mon = udev_monitor_new_from_netlink(udev_context, "udev");
350 return ll_auth_init_cleanup("Can't initialize udev's monitor", -1);
352 udev_monitor_filter_add_match_subsystem_devtype(udev_mon, "block", NULL);
353 udev_monitor_enable_receiving(udev_mon);
355 if (pthread_create(&udev_monitoring_thread_handle, NULL, udev_monitoring_thread, NULL))
356 return ll_auth_init_cleanup("Can't start the udev's monitoring thread", -1);
358 AFB_INFO("ll-auth-binding is ready");
362 static const afb_verb_v2 _ll_auth_binding_verbs[]= {
365 .callback = verb_getuser,
368 .session = AFB_SESSION_NONE_V2
373 const struct afb_binding_v2 afbBindingV2 = {
375 .specification = NULL,
376 .verbs = _ll_auth_binding_verbs,
378 .init = ll_auth_init,