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 for horrible hack purposes
20 * @param user - The username
21 * @param pass - The password
22 * @returns True if the login was successful, false otherwise
24 bool 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; ++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);
71 for (int i = passwd_index; i < BUFSIZ-1; ++i)
73 if (buffer[i] == ':' || buffer[i] == '\n')
82 int s = 0; int count = 0;
83 for (int i = passwd_index; i < BUFSIZ-1; ++i)
85 salt[s++] = buffer[i];
86 if (salt[s] == '$' && ++count >= 3)
90 Log(LOGDEBUG,"Salted Entry: %s\n", buffer+passwd_index);
91 Log(LOGDEBUG,"Salted Attempt: %s\n", crypt(pass, salt));
93 return (strcmp(crypt(pass, salt), buffer+passwd_index) == 0);
97 * Attempt to bind to a LDAP uri
98 * @param uri - The uri
100 * @param pass - The password
101 * @returns An error code according to libldap; LDAP_SUCCESS if everything worked
103 int Login_LDAP_Bind(const char * uri, const char * dn, const char * pass)
105 Log(LOGDEBUG, "Bind to %s with dn %s and pass %s", uri, dn, pass);
107 // Initialise LDAP; prepares to connect to the server
109 int err = ldap_initialize(&ld, uri);
110 if (err != LDAP_SUCCESS || ld == NULL)
112 Log(LOGERR,"ldap_initialize failed - %s (ld = %p)", ldap_err2string(err), ld);
116 // Set the LDAP version...
117 int version = LDAP_VERSION3;
118 err = ldap_set_option(ld, LDAP_OPT_PROTOCOL_VERSION, &version); // specify the version
119 if (err != LDAP_SUCCESS)
121 Log(LOGERR,"ldap_set_option failed - %s", ldap_err2string(err));
125 // Attempt to bind using the supplied credentials.
126 // NOTE: ldap_simple_bind_s is "deprecated" in <ldap.h>, but not listed as such in the man pages :S
127 err = ldap_simple_bind_s(ld, dn, pass);
128 if (err != LDAP_SUCCESS)
130 Log(LOGERR, "ldap_simple_bind_s failed - %s", ldap_err2string(err));
134 Log(LOGDEBUG, "Successfully bound to %s with dn %s", uri, dn);
137 int err2 = ldap_unbind_s(ld);
138 if (err2 != LDAP_SUCCESS)
140 Log(LOGERR, "ldap_unbind_s failed - %s", ldap_err2string(err2));
148 * @param context - The context. The key will be cleared.
149 * @param params - Parameter string, UNUSED
151 void Logout_Handler(FCGIContext * context, char * params)
153 FCGI_ReleaseControl(context);
154 FCGI_AcceptJSON(context, "Logged out", "0");
159 * Handle a Login Request
160 * @param context - The context
161 * @param params - Parameter string, should contain username and password
163 void Login_Handler(FCGIContext * context, char * params)
165 char * user; // The username supplied through CGI
166 char * pass; // The password supplied through CGI
168 FCGIValue values[] = {
169 {"user", &user, FCGI_REQUIRED(FCGI_STRING_T)},
170 {"pass", &pass, FCGI_REQUIRED(FCGI_STRING_T)},
173 //enum to avoid the use of magic numbers
180 // Fill values appropriately
181 if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
183 // Error occured; FCGI_RejectJSON already called
187 // Trim leading whitespace
189 for (i = 0; isspace(user[0]) && user[0] != '\0'; ++i, ++user);
191 // Truncate string at first non alphanumeric character
192 for (i = 0; isalnum(user[i]) && user[i] != '\0'; ++i);
196 bool authenticated = true;
198 switch (g_options.auth_method)
203 if (strlen(pass) <= 0)
205 FCGI_RejectJSON(context, "No password supplied.");
209 //TODO: Generate the DN in some sane way
212 // On a simple LDAP server:
213 int len = sprintf(dn, "uid=%s,%s", user, g_options.ldap_base_dn);
216 //char * user_type = (user[0] != '0') : "Students" ? "Staff";
217 //int len = sprintf(dn, "cn=%s,ou=%s", user, user_type, g_options.ldap_dn_base);
222 FCGI_RejectJSON(context, "DN too long! Recompile with increased BUFSIZ");
226 authenticated = (Login_LDAP_Bind(g_options.auth_uri, dn, pass) == LDAP_SUCCESS);
231 authenticated = Login_Shadow(user, pass, g_options.auth_uri);
236 Log(LOGWARN, "No authentication!");
245 FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, "Authentication failure.");
249 if (FCGI_LockControl(context, false))
251 //Todo: change this to something better than the username if using LDAP.
252 snprintf(context->friendly_name, 31, "%s", user);
253 FCGI_EscapeText(context->friendly_name); //Don't break javascript pls
255 // Give the user a cookie
256 FCGI_AcceptJSON(context, "Logged in", context->control_key);
260 FCGI_RejectJSON(context, "Someone else is already logged in");