-
Notifications
You must be signed in to change notification settings - Fork 28
Expand file tree
/
Copy pathuserManagement.cpp
More file actions
349 lines (279 loc) · 12.3 KB
/
userManagement.cpp
File metadata and controls
349 lines (279 loc) · 12.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
/*
userManagement.cpp
This file is part of Multitasking Esp32 HTTP FTP Telnet servers for Arduino project: https://github.com/BojanJurca/Multitasking-Esp32-HTTP-FTP-Telnet-servers-for-Arduino
UNIX-like user management.
April 27, 2026, Bojan Jurca
*/
#include "userManagement.h"
userManagement_t::userManagement_t (threadSafeFS::FS& fileSystem) : __fileSystem__ (fileSystem) {
// create /etc/passwd if it doesn't exist
if (!__fileSystem__.isFile ("/etc/passwd")) {
// create /etc directory
if (!__fileSystem__.isDirectory ("/etc"))
__fileSystem__.mkdir ("/etc");
bool created = __writePasswd__ ("root:::::/:\r\n"
"webadmin:::::/var/www/html/:");
cout << (created ? "/etc/passwd created\n" : "error creating /etc/passwd\n");
}
// create /etc/shadow if it doesn't exist
if (!__fileSystem__.isFile ("/etc/shadow")) {
// /etc directory already exists
bool created = __writeShadow__ ("root:$5$" DEFAULT_ROOT_PASSWORD_SHA ":::::::\r\n"
"webadmin:$5$" DEFAULT_WEBADMIN_PASSWORD_SHA ":::::::");
cout << (created ? "/etc/shadow created\n" : "error creating /etc/shadow\n");
}
};
// scan through /etc/shadow file for (user name, pasword) pair and return true if found
bool userManagement_t::checkUserNameAndPassword (const char *userName, const char *password) {
// initial checking
if (strlen (userName) > USER_PASSWORD_MAX_LENGTH || strlen (password) > USER_PASSWORD_MAX_LENGTH)
return false;
// read /etc/shadow
char buffer [MAX_ETC_SHADOW_SIZE + 2] = {};
buffer [0] = '\n';
if (!__readShadow__ (buffer + 1))
return false;
// find user's record in the buffer
char srchStr [USER_PASSWORD_MAX_LENGTH + 65 + 7];
sprintf (srchStr, "\n%s:$5$", userName);
__sha256__ (srchStr + strlen (srchStr), 65, password);
strcat (srchStr, ":"); // we get something like "\nroot:$5$de362bbdf11f2df30d12f318eeed87b8ee9e0c69c8ba61ed9a57695bbd91e481:"
if (strstr (buffer, srchStr)) // the beginning of the line matches the user name and sha of password
return true; // success
return false;
}
// returns "/" for full access
// something like "/home/name" for limited access
// "" for no access
Cstring<255> userManagement_t::getHomeDirectory (const char *userName) {
Cstring<255> homeDirectory;
if (strlen (userName) > USER_PASSWORD_MAX_LENGTH)
return "";
// read /etc/passwd
char buffer [MAX_ETC_PASSWD_SIZE + 3] = {};
buffer [0] = '\n';
if (!__readPasswd__ (buffer + 1))
return "";
// find user's record in the buffer
char srchStr [USER_PASSWORD_MAX_LENGTH + 2];
sprintf (srchStr, "\n%s:", userName); // we get something like \nroot:$5$
char *p = strstr (buffer, srchStr);
if (!p)
return ""; // user not found in /etc/passwd
for (int i = 0; i < 5; i++)
if (!(p = strchr (p + 1, ':')))
return "";
// the following uses too much stack:
if (sscanf (p + 1, "%255[^:]", (char *) (homeDirectory.c_str ())) <= 0)
return "";
return homeDirectory;
}
// bool passwd (userName, newPassword) assignes a new password for the user by writing it's SHA value into /etc/shadow file, return success
bool userManagement_t::passwd (const char *userName, const char *newPassword) {
// initial checking
if (strlen (newPassword) > USER_PASSWORD_MAX_LENGTH)
return false;
// read /etc/shadow
char buffer [MAX_ETC_SHADOW_SIZE + 2] = {};
buffer [0] = '\n';
if (!__readShadow__ (buffer + 1))
return false;
// find user's record in the buffer
char srchStr [USER_PASSWORD_MAX_LENGTH + 6];
sprintf (srchStr, "\n%s:$5$", userName);
char *p = strstr (buffer, srchStr);
if (!p || p - buffer > MAX_ETC_SHADOW_SIZE - 65)
return false; // user not found in /etc/shadow
// replace SHA of old password with SHA of new password
char newPasswordSHA [65];
__sha256__ (newPasswordSHA, 65, newPassword);
memcpy (p + strlen (srchStr), newPasswordSHA, 64);
// write /etc/shadow
if (!__writeShadow__ (buffer + 1))
return false;
return true; // success
}
// char *userAdd (userName, userId, userHomeDirectory) adds userName, userId, userHomeDirectory to /etc/passwd and /etc/shadow, returns success or error message
const char *userManagement_t::userAdd (const char *userName, const char *userHomeDirectory) {
if (!userName || strlen (userName) < 1) return "Missing user name";
if (strlen (userName) > USER_PASSWORD_MAX_LENGTH) return "User name too long";
if (strstr (userName, ":")) return "User name may not contain ':' character";
if (!userHomeDirectory || strlen (userHomeDirectory) < 1) return "Missing user's home directory";
if (strlen (userHomeDirectory) > 255) return "User's home directory name too long";
Cstring<255> homeDirectory = userHomeDirectory;
{
// read /etc/passwd
Cstring<MAX_ETC_PASSWD_SIZE + 1> buffer = "\n";
if (!__readPasswd__ ((char *) (buffer.c_str () + 1)))
return "Can't read /etc/passwd";
// find user's record in the buffer
char srchStr [USER_PASSWORD_MAX_LENGTH + 2];
sprintf (srchStr, "\n%s:", userName); // we get something like \nroot:$5$
char *p = strstr (buffer.c_str (), srchStr);
if (p)
return "User with this name already exists";
buffer += "\r\n";
buffer += userName;
buffer += ":::::";
buffer += homeDirectory;
buffer += ":";
if (buffer.errorFlags ())
return "Not enough space in /etc/passwd";
// write /etc/passwd
if (!__writePasswd__ ((char *) (buffer.c_str () + 1)))
return "Can't write /etc/passwd";
}
{
Cstring<MAX_ETC_SHADOW_SIZE + 1> buffer = "\n";
if (!__readShadow__ ((char *) (buffer.c_str () + 1)))
return "Can't read /etc/shadow";
// find user's record in the buffer
char srchStr [USER_PASSWORD_MAX_LENGTH + 2];
sprintf (srchStr, "\n%s:", userName); // we get something like \nroot:$5$
char *p = strstr (buffer.c_str (), srchStr);
if (p) {
memcpy (p + strlen (srchStr), DEFAULT_USER_PASSWORD_SHA, 65);
} else {
buffer += "\r\n";
buffer += userName;
buffer += ":$5$";
buffer += DEFAULT_USER_PASSWORD_SHA;
buffer += ":::::::";
if (buffer.errorFlags ())
return "Not enough space in /etc/shadow";
// write /etc/passwd
if (!__writeShadow__ ((char *) (buffer.c_str () + 1)))
return "Can't write /etc/shadow";
}
}
return "User created with default password '" DEFAULT_USER_PASSWORD "'";
}
// char *userDel (userName) deletes userName from /etc/passwd and /etc/shadow, returns success or error message
const char *userManagement_t::userDel (const char *userName) {
if (!userName || strlen (userName) < 1) return "Missing user name";
if (strlen (userName) > USER_PASSWORD_MAX_LENGTH) return "User name too long";
{
// read /etc/passwd
char buffer [MAX_ETC_PASSWD_SIZE + 3] = {};
buffer [0] = '\n';
if (!__readPasswd__ (buffer + 1))
return "";
// find user's record in the buffer
char srchStr [USER_PASSWORD_MAX_LENGTH + 2];
sprintf (srchStr, "\n%s:", userName); // we get something like \nroot:$5$
char *p = strstr (buffer, srchStr);
if (!p)
return "User with this name doesn't exist";
p++; // p now points directly to username
// find the end of the line
char *q = p;
while (*q > '\n')
q++;
if (*q) {
// end of line found
while (*q && *q < ' ')
q++;
strcpy (p, q);
} else {
// end of line not found - this is the last record, close and rtrim the buffer
for (*p = 0; p > buffer && *p <= ' '; p--)
*p = 0;
}
// write /etc/passwd
if (!__writePasswd__ (buffer + 1))
return "Can't write /etc/passwd";
}
{
// read /etc/shadow
char buffer [MAX_ETC_SHADOW_SIZE + 2] = {};
buffer [0] = '\n';
if (!__readShadow__ (buffer + 1))
return "Can't read /etc/shadow";
// find user's record in the buffer
char srchStr [USER_PASSWORD_MAX_LENGTH + 6];
sprintf (srchStr, "\n%s:$5$", userName);
char *p = strstr (buffer, srchStr);
if (p) {
p++; // p now points directly to username
// find the end of the line
char *q = p;
while (*q > '\n')
q++;
if (*q) {
// end of line found
while (*q && *q < ' ')
q++;
strcpy (p, q);
} else {
// end of line not found - this is the last record, close and rtrim the buffer
for (*p = 0; p > buffer && *p <= ' '; p--)
*p = 0;
}
// write /etc/shadow
if (!__writeShadow__ (buffer + 1))
return "Can't write /etc/shadow";
}
}
return "User deleted";
}
// converts clearText to 256 bit SHA, returns character representation in hexadecimal format of hash value
bool userManagement_t::__sha256__ (char *buffer, size_t bufferSize, const char *clearText) {
*buffer = 0;
byte shaByteResult [32];
char shaCharResult [65];
mbedtls_md_context_t ctx;
mbedtls_md_type_t md_type = MBEDTLS_MD_SHA256;
mbedtls_md_init (&ctx);
mbedtls_md_setup (&ctx, mbedtls_md_info_from_type (md_type), 0);
mbedtls_md_starts (&ctx);
mbedtls_md_update (&ctx, (const unsigned char *) clearText, strlen (clearText));
mbedtls_md_finish (&ctx, shaByteResult);
mbedtls_md_free (&ctx);
for (int i = 0; i < 32; i++)
sprintf (shaCharResult + 2 * i, "%02x", (int) shaByteResult [i]);
if (bufferSize > strlen (shaCharResult)) {
strcpy (buffer, shaCharResult);
return true;
}
return false;
}
bool userManagement_t::__writePasswd__ (const char *buffer) { // buffer of MAX_ETC_PASSWD_SIZE bytes
threadSafeFS::File f = __fileSystem__.open ("/etc/passwd", "w");
if (f && !f.isDirectory ())
if (f.print (buffer) > 0)
return true;
return false;
}
bool userManagement_t::__readPasswd__ (char *buffer) { // buffer of MAX_ETC_PASSWD_SIZE bytes
threadSafeFS::File f = __fileSystem__.open ("/etc/passwd", "r");
if (!f || f.isDirectory ())
return false;
size_t bytesRead = f.read ((byte *) buffer, MAX_ETC_PASSWD_SIZE);
if (bytesRead == MAX_ETC_PASSWD_SIZE)
return false; // file contet too long for buffer
// buffer [bytesRead] = 0; // close C string
// close and rtrim buffer
for (char *p = buffer + bytesRead; p > buffer && *p <= ' '; p--)
*p = 0;
return true;
}
bool userManagement_t::__writeShadow__ (const char *buffer) { // buffer of MAX_ETC_SHADOW_SIZE bytes
threadSafeFS::File f = __fileSystem__.open ("/etc/shadow", "w");
if (f && !f.isDirectory ())
if (f.print (buffer) > 0)
return true;
return false;
}
bool userManagement_t::__readShadow__ (char *buffer) { // buffer of MAX_ETC_SHADOW_SIZE bytes
threadSafeFS::File f = __fileSystem__.open ("/etc/shadow", "r");
if (!f || f.isDirectory ())
return false;
size_t bytesRead = f.read ((byte *) buffer, MAX_ETC_SHADOW_SIZE);
if (bytesRead == MAX_ETC_SHADOW_SIZE)
return false; // file contet too long for buffer
// buffer [bytesRead] = 0; // close C string
// close and rtrim buffer
for (char *p = buffer + bytesRead; p > buffer && *p <= ' '; p--)
*p = 0;
return true;
}