Merge branch 'master' of https://github.com/szmoore/MCTX3420.git
[matches/MCTX3420.git] / server / login.c
1 /**
2  * @file login.c
3  * @brief Implementation of Login related functionality
4  */
5
6
7
8
9 #include "login.h"
10 #include "options.h"
11 #include <ctype.h>
12 #include <unistd.h>
13
14 #define LDAP_DEPRECATED 1 // Required to use ldap_simple_bind_s
15 #include <ldap.h>
16
17 /**
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
23  */
24 bool Login_Shadow(const char * user, const char * pass, const char * shadow)
25 {
26         if (strlen(user) + strlen(pass) >= BUFSIZ-1)
27         {
28                 Log(LOGERR, "User/Password too long!\n");
29                 return false;
30         }
31
32         FILE * f = fopen(shadow, "r");
33         if (f == NULL)
34         {
35                 Log(LOGERR,"Can't open %s - %s\n", shadow, strerror(errno));
36                 return false;
37         }
38
39         char buffer[BUFSIZ];
40         int passwd_index = -1;
41
42         while (fgets(buffer, BUFSIZ, f) != NULL) // NOTE: Restrict username+password strings to BUFSIZ... what could possibly go wrong?
43         {
44
45                 Log(LOGDEBUG,"Scanning %d: %s", strlen(buffer), buffer);
46         
47                 for (int i = 0; i < BUFSIZ-1; ++i)
48                 {
49                         if (buffer[i] == ':')
50                         {
51                                 buffer[i] = '\0';
52                                 passwd_index = i+1;
53                                 break;
54                         }
55                 }
56
57                 if (strcmp(user,buffer) == 0)
58                 {
59                         Log(LOGDEBUG,"User matches! %s\n", buffer);
60                         break;
61                 } 
62                 passwd_index = -1;
63         }
64
65         if (passwd_index <= 0)
66         {
67                 Log(LOGDEBUG,"No user found matching %s\n", user);
68                 return false;
69         }
70
71         for (int i = passwd_index; i < BUFSIZ-1; ++i)
72         {
73                 if (buffer[i] == ':' || buffer[i] == '\n')
74                 {
75                         buffer[i] = '\0';
76                         
77                 }
78         }
79         
80         // Determine the salt
81         char salt[BUFSIZ];
82         int s = 0; int count = 0;
83         for (int i = passwd_index; i < BUFSIZ-1; ++i)
84         {
85                 salt[s++] = buffer[i];
86                 if (salt[s] == '$' && ++count >= 3)
87                         break;
88         }
89
90         Log(LOGDEBUG,"Salted Entry: %s\n", buffer+passwd_index);
91         Log(LOGDEBUG,"Salted Attempt: %s\n", crypt(pass, salt));
92         
93         return (strcmp(crypt(pass, salt), buffer+passwd_index) == 0);
94 }
95
96 /**
97  * Attempt to bind to a LDAP uri
98  * @param uri - The uri
99  * @param dn - The DN
100  * @param pass - The password
101  * @returns An error code according to libldap; LDAP_SUCCESS if everything worked
102  */
103 int Login_LDAP_Bind(const char * uri, const char * dn, const char * pass)
104 {
105         Log(LOGDEBUG, "Bind to %s with dn %s and pass %s", uri, dn, pass);
106
107         // Initialise LDAP; prepares to connect to the server
108         LDAP * ld = NULL;
109         int err = ldap_initialize(&ld, uri);
110         if (err != LDAP_SUCCESS || ld == NULL)
111         {
112                 Log(LOGERR,"ldap_initialize failed - %s (ld = %p)", ldap_err2string(err), ld);
113                 return err;
114         }
115
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)
120         {
121                 Log(LOGERR,"ldap_set_option failed - %s", ldap_err2string(err));
122                 return err;
123         }
124
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)
129         {
130                 Log(LOGERR, "ldap_simple_bind_s failed - %s", ldap_err2string(err));
131         }
132         else
133         {
134                 Log(LOGDEBUG, "Successfully bound to %s with dn %s", uri, dn);
135         }
136
137         int err2 = ldap_unbind_s(ld);
138         if (err2 != LDAP_SUCCESS)
139         {
140                 Log(LOGERR, "ldap_unbind_s failed - %s", ldap_err2string(err2));
141                 err = err2;
142         }
143         return err;
144 }
145
146 /**
147  * Logout
148  * @param context - The context. The key will be cleared.
149  * @param params - Parameter string, UNUSED
150  */
151 void Logout_Handler(FCGIContext * context, char * params)
152 {
153         FCGI_ReleaseControl(context);
154         FCGI_AcceptJSON(context, "Logged out", "0");
155 }
156
157
158 /**
159  * Handle a Login Request
160  * @param context - The context
161  * @param params - Parameter string, should contain username and password
162  */
163 void Login_Handler(FCGIContext * context, char * params)
164 {
165         char * user; // The username supplied through CGI
166         char * pass; // The password supplied through CGI
167
168         FCGIValue values[] = {
169                 {"user", &user, FCGI_REQUIRED(FCGI_STRING_T)},
170                 {"pass", &pass, FCGI_REQUIRED(FCGI_STRING_T)},
171         };
172
173         //enum to avoid the use of magic numbers
174         typedef enum {
175                 USER,
176                 PASS,
177                 LOGOUT
178         } LoginParams;
179
180         // Fill values appropriately
181         if (!FCGI_ParseRequest(context, params, values, sizeof(values)/sizeof(FCGIValue)))
182         {
183                 // Error occured; FCGI_RejectJSON already called
184                 return;
185         }
186
187         // Trim leading whitespace
188         int i = 0;
189         for (i = 0; isspace(user[0]) && user[0] != '\0'; ++i, ++user);
190
191         // Truncate string at first non alphanumeric character
192         for (i = 0; isalnum(user[i]) && user[i] != '\0'; ++i);
193         user[i] = '\0';
194
195         
196         bool authenticated = true;
197         
198         switch (g_options.auth_method)
199         {
200
201                 case AUTH_LDAP:
202                 {
203                         if (strlen(pass) <= 0)
204                         {
205                                 FCGI_RejectJSON(context, "No password supplied.");
206                                 return;
207                         }
208
209                         //TODO: Generate the DN in some sane way
210                         char dn[BUFSIZ];
211                 
212                         // On a simple LDAP server:
213                         //int len = sprintf(dn, "uid=%s,%s", user, g_options.ldap_base_dn);
214         
215                         // At UWA (hooray)
216                         char * user_type = "Students";
217                         if (user[0] == '0')
218                                 user_type = "Staff";
219                         int len = sprintf(dn, "cn=%s,ou=%s,%s", user, user_type, g_options.ldap_base_dn);
220                 
221
222                         if (len >= BUFSIZ)
223                         {
224                                 FCGI_RejectJSON(context, "DN too long! Recompile with increased BUFSIZ");
225                                 return;
226                         }
227                 
228                         authenticated = (Login_LDAP_Bind(g_options.auth_uri, dn, pass) == LDAP_SUCCESS);
229                         break;
230                 }
231                 case AUTH_SHADOW:
232                 {
233                         authenticated = Login_Shadow(user, pass, g_options.auth_uri);
234                         break;
235                 }
236                 default:
237                 {
238                         Log(LOGWARN, "No authentication!");
239                         break;
240                 }
241         }
242                 
243         // error check  
244         
245         if (!authenticated)
246         {
247                 FCGI_RejectJSONEx(context, STATUS_UNAUTHORIZED, "Authentication failure.");
248         }
249         else
250         {
251                 if (FCGI_LockControl(context, false))
252                 {
253                         //Todo: change this to something better than the username if using LDAP.
254                         snprintf(context->friendly_name, 31, "%s", user);
255                         FCGI_EscapeText(context->friendly_name); //Don't break javascript pls
256
257                         // Give the user a cookie
258                         FCGI_AcceptJSON(context, "Logged in", context->control_key);
259                 }
260                 else
261                 {
262                         FCGI_RejectJSON(context, "Someone else is already logged in");
263                 }
264         }
265 }

UCC git Repository :: git.ucc.asn.au