3 * @brief Implementation of Login related functionality
14 #define LDAP_DEPRECATED 1 // Required to use ldap_simple_bind_s
18 * Attempt to login using a file formatted like /etc/shadow
19 * This is here... because all better options have been exhausted
20 * @param user - The username
21 * @param pass - The password
22 * @returns Privelage level of the user or USER_UNAUTH for failure to authenticate
24 UserType Login_Shadow(const char * user, const char * pass, const char * shadow)
26 if (strlen(user) + strlen(pass) >= BUFSIZ-1)
28 Log(LOGERR, "User/Password too long!\n");
32 FILE * f = fopen(shadow, "r");
35 Log(LOGERR,"Can't open %s - %s\n", shadow, strerror(errno));
40 int passwd_index = -1;
42 while (fgets(buffer, BUFSIZ, f) != NULL) // NOTE: Restrict username+password strings to BUFSIZ... what could possibly go wrong?
45 //Log(LOGDEBUG,"Scanning %d: %s", strlen(buffer), buffer);
47 for (int i = 0; i < BUFSIZ-1 && buffer[i] != '\0'; ++i)
57 if (strcmp(user,buffer) == 0)
59 //Log(LOGDEBUG,"User matches! %s\n", buffer);
65 if (passwd_index <= 0)
67 //Log(LOGDEBUG,"No user found matching %s\n", user);
72 for (int i = passwd_index; i < BUFSIZ-1 && buffer[i] != '\0'; ++i)
79 if (buffer[i] == '\n')
82 char * end = buffer+gid_index;
83 UserType user_type = USER_NORMAL;
84 if (gid_index > passwd_index && gid_index < BUFSIZ-1)
86 int gid = strtol(buffer+gid_index, &end,10);
87 Log(LOGDEBUG, "Usertype %d %s", gid, buffer+gid_index);
88 if (*end == '\0' && gid == 0)
90 Log(LOGDEBUG, "Admin");
91 user_type = USER_ADMIN;
97 int s = 0; int count = 0;
98 for (int i = passwd_index; i < BUFSIZ-1; ++i)
100 salt[s++] = buffer[i];
101 if (salt[s] == '$' && ++count >= 3)
105 // Log(LOGDEBUG,"Salted Entry: %s\n", buffer+passwd_index);
106 // Log(LOGDEBUG,"Salted Attempt: %s\n", crypt(pass, salt));
108 if (strcmp(crypt(pass, salt), buffer+passwd_index) == 0)
116 * Attempt to bind to a LDAP uri
117 * @param uri - The uri
119 * @param pass - The password
120 * @returns An error code according to libldap; LDAP_SUCCESS if everything worked
122 int Login_LDAP_Bind(const char * uri, const char * dn, const char * pass)
124 Log(LOGDEBUG, "Bind to %s with dn %s and pass %s", uri, dn, pass);
126 // Initialise LDAP; prepares to connect to the server
128 int err = ldap_initialize(&ld, uri);
129 if (err != LDAP_SUCCESS || ld == NULL)
131 Log(LOGERR,"ldap_initialize failed - %s (ld = %p)", ldap_err2string(err), ld);
135 // Set the LDAP version...
136 int version = LDAP_VERSION3;
137 err = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version); // specify the version
138 if (err != LDAP_SUCCESS)
140 Log(LOGERR,"ldap_set_option failed - %s", ldap_err2string(err));
144 // Attempt to bind using the supplied credentials.
145 // NOTE: ldap_simple_bind_s is "deprecated" in <ldap.h>, but not listed as such in the man pages :S
146 err = ldap_simple_bind_s(ld, dn, pass);
147 if (err != LDAP_SUCCESS)
149 Log(LOGERR, "ldap_simple_bind_s failed - %s", ldap_err2string(err));
153 Log(LOGDEBUG, "Successfully bound to %s with dn %s", uri, dn);
156 int err2 = ldap_unbind_s(ld);
157 if (err2 != LDAP_SUCCESS)
159 Log(LOGERR, "ldap_unbind_s failed - %s", ldap_err2string(err2));
167 * @param context - The context. The key will be cleared.
168 * @param params - Parameter string, UNUSED
170 void Logout_Handler(FCGIContext * context, char * params)
172 FCGI_ReleaseControl(context);
173 FCGI_SendControlCookie(context, false); //Unset the cookie
174 FCGI_AcceptJSON(context, "Logged out");
179 * Handle a Login Request
180 * @param context - The context
181 * @param params - Parameter string, should contain username and password.
182 * NOTE: Care should be taken when using params, as it is
183 * completely unescaped. Do not log or use it without
186 void Login_Handler(FCGIContext * context, char * params)
188 char * user; // The username supplied through CGI
189 char * pass; // The password supplied through CGI
191 FCGIValue values[] = {
192 {"user", &user, FCGI_REQUIRED(FCGI_STRING_T)},
193 {"pass", &pass, FCGI_REQUIRED(FCGI_STRING_T)},
196 //enum to avoid the use of magic numbers
203 // Fill values appropriately
204 if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
206 // Error occured; FCGI_RejectJSON already called
210 // Trim leading whitespace
212 for (i = 0; isspace(user[0]) && user[0] != '\0'; ++i, ++user);
214 // Truncate string at first non alphanumeric character
215 for (i = 0; isalnum(user[i]) && user[i] != '\0'; ++i);
219 UserType user_type = USER_UNAUTH;
221 switch (g_options.auth_method)
228 FCGI_RejectJSON(context, "No password supplied.");
232 //TODO: Generate the DN in some sane way
235 // On a simple LDAP server:
236 //int len = sprintf(dn, "uid=%s,%s", user, g_options.ldap_base_dn);
239 char * user_group = "Students";
241 user_group = "Staff";
242 int len = sprintf(dn, "cn=%s,ou=%s,%s", user, user_group, g_options.ldap_base_dn);
247 FCGI_RejectJSON(context, "DN too long! Recompile with increased BUFSIZ");
251 if (Login_LDAP_Bind(g_options.auth_uri, dn, pass) == LDAP_SUCCESS)
254 user_type = USER_ADMIN;
256 user_type = USER_NORMAL;
262 user_type = Login_Shadow(user, pass, g_options.auth_uri);
267 Log(LOGWARN, "No authentication!");
268 user_type = USER_ADMIN;
275 if (user_type == USER_UNAUTH)
277 Log(LOGDEBUG, "Authentication failure for %s", user);
278 FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, "Authentication failure.");
282 // Try and gain control over the system
283 if (FCGI_LockControl(context, user, user_type))
285 FCGI_EscapeText(context->user_name); //Don't break javascript pls
286 // Give the user a cookie
287 FCGI_SendControlCookie(context, true); //Send the control key
288 FCGI_AcceptJSON(context, "Logged in");
289 Log(LOGDEBUG, "Successful authentication for %s", user);
293 Log(LOGDEBUG, "%s successfully authenticated but system was in use by %s", user, context->user_name);
294 FCGI_RejectJSON(context, "Someone else is already logged in (and you are not an admin)");