1 /*
2 * Copyright (C) 2013 Nikos Mavrogiannopoulos
3 *
4 * This file is part of ocserv.
5 *
6 * ocserv is free software: you can redistribute it and/or modify it
7 * under the terms of the GNU General Public License as published by
8 * the Free Software Foundation, either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * ocserv is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include <config.h>
21
22 #include <stdio.h>
23 #include <string.h>
24 #include <stdlib.h>
25 #include <stdint.h>
26 #ifndef _XOPEN_SOURCE
27 # define _XOPEN_SOURCE
28 #endif
29 #include <unistd.h>
30 #include <gnutls/gnutls.h>
31 #include <gnutls/crypto.h> /* for random */
32 #include <sys/types.h>
33 #include <sys/stat.h>
34 #include <getopt.h>
35 #ifdef HAVE_CRYPT_H
36 /* libcrypt in Fedora28 does not provide prototype
37 * in unistd.h */
38 # include <crypt.h>
39 #endif
40
41 /* Gnulib portability files. */
42 #include <getpass.h>
43 #include <minmax.h>
44
45 #define DEFAULT_OCPASSWD "/usr/local/etc/ocserv/ocpasswd"
46
47 static const char alphabet[] =
48 "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./";
49
50 #define SALT_SIZE 16
51 static void
crypt_int(const char * fpasswd,const char * username,const char * groupname,const char * passwd)52 crypt_int(const char *fpasswd, const char *username, const char *groupname,
53 const char *passwd)
54 {
55 uint8_t _salt[SALT_SIZE];
56 char salt[SALT_SIZE+16];
57 char *p, *cr_passwd;
58 char *tmp_passwd;
59 unsigned i;
60 unsigned fpasswd_len = strlen(fpasswd);
61 unsigned tmp_passwd_len;
62 unsigned username_len = strlen(username);
63 struct stat st;
64 FILE *fd, *fd2;
65 char *line = NULL;
66 size_t line_size;
67 ssize_t len, l;
68 int ret;
69
70 ret = gnutls_rnd(GNUTLS_RND_NONCE, _salt, sizeof(_salt));
71 if (ret < 0) {
72 fprintf(stderr, "Error generating nonce: %s\n",
73 gnutls_strerror(ret));
74 exit(1);
75 }
76
77 #ifdef TRY_SHA2_CRYPT
78 strcpy(salt, "$5$");
79 #else
80 strcpy(salt, "$1$");
81 #endif
82 p = salt + 3;
83
84 for (i = 0; i < sizeof(_salt); i++) {
85 *p = alphabet[_salt[i] % (sizeof(alphabet) - 1)];
86 p++;
87 }
88 *p = '$';
89 p++;
90 *p = 0;
91 p++;
92
93 cr_passwd = crypt(passwd, salt);
94 if (cr_passwd == NULL) { /* try MD5 */
95 salt[1] = '1';
96 cr_passwd = crypt(passwd, salt);
97 }
98 if (cr_passwd == NULL) {
99 fprintf(stderr, "Error in crypt().\n");
100 exit(1);
101 }
102
103 tmp_passwd_len = fpasswd_len + 5;
104 tmp_passwd = malloc(tmp_passwd_len);
105 if (tmp_passwd == NULL) {
106 fprintf(stderr, "memory error\n");
107 exit(1);
108 }
109
110 snprintf(tmp_passwd, tmp_passwd_len, "%s.tmp", fpasswd);
111 if (stat(tmp_passwd, &st) != -1) {
112 fprintf(stderr, "file '%s' is locked.\n", fpasswd);
113 exit(1);
114 }
115
116 fd2 = fopen(tmp_passwd, "w");
117 if (fd2 == NULL) {
118 fprintf(stderr, "Cannot open '%s' for writing.\n", tmp_passwd);
119 exit(1);
120 }
121
122 fd = fopen(fpasswd, "r");
123 if (fd == NULL) {
124 fprintf(fd2, "%s:%s:%s\n", username, groupname, cr_passwd);
125 } else {
126 int found = 0;
127 while ((len = getline(&line, &line_size, fd)) > 0) {
128 p = strchr(line, ':');
129 if (p == NULL)
130 continue;
131
132 l = p-line;
133 if (l == username_len && strncmp(line, username, l) == 0) {
134 fprintf(fd2, "%s:%s:%s\n", username, groupname, cr_passwd);
135 found = 1;
136 } else {
137 fwrite(line, 1, len, fd2);
138 }
139 }
140 free(line);
141 fclose(fd);
142
143 if (found == 0)
144 fprintf(fd2, "%s:%s:%s\n", username, groupname, cr_passwd);
145 }
146
147 fclose(fd2);
148
149 ret = rename(tmp_passwd, fpasswd);
150 if (ret < 0) {
151 fprintf(stderr, "Cannot write to '%s'.\n", fpasswd);
152 exit(1);
153 }
154 free(tmp_passwd);
155 }
156
157 static void
delete_user(const char * fpasswd,const char * username)158 delete_user(const char *fpasswd, const char *username)
159 {
160 FILE * fd, *fd2;
161 char *tmp_passwd;
162 char *line, *p;
163 unsigned fpasswd_len = strlen(fpasswd);
164 unsigned tmp_passwd_len;
165 unsigned username_len = strlen(username);
166 int ret;
167 ssize_t len, l;
168 size_t line_size;
169 struct stat st;
170
171 tmp_passwd_len = fpasswd_len + 5;
172 tmp_passwd = malloc(tmp_passwd_len);
173
174 snprintf(tmp_passwd, tmp_passwd_len, "%s.tmp", fpasswd);
175 if (stat(tmp_passwd, &st) != -1) {
176 fprintf(stderr, "file '%s' is locked.\n", fpasswd);
177 exit(1);
178 }
179
180 fd = fopen(fpasswd, "r");
181 if (fd == NULL) {
182 fprintf(stderr, "Cannot open '%s' for reading.\n", fpasswd);
183 exit(1);
184 }
185
186 fd2 = fopen(tmp_passwd, "w");
187 if (fd2 == NULL) {
188 fprintf(stderr, "Cannot open '%s' for writing.\n", tmp_passwd);
189 exit(1);
190 }
191
192 line = NULL;
193 while ((len = getline(&line, &line_size, fd)) > 0) {
194 p = strchr(line, ':');
195 if (p == NULL)
196 continue;
197
198 l = p-line;
199 if (l == username_len && strncmp(line, username, l) == 0) {
200 continue;
201 } else {
202 fwrite(line, 1, len, fd2);
203 }
204 }
205
206 free(line);
207 fclose(fd);
208 fclose(fd2);
209
210 ret = rename(tmp_passwd, fpasswd);
211 if (ret == -1) {
212 fprintf(stderr, "Cannot write to '%s'.\n", fpasswd);
213 exit(1);
214 }
215 free(tmp_passwd);
216 }
217
218 static void
lock_user(const char * fpasswd,const char * username)219 lock_user(const char *fpasswd, const char *username)
220 {
221 FILE * fd, *fd2;
222 char *tmp_passwd;
223 char *line, *p;
224 unsigned fpasswd_len = strlen(fpasswd);
225 unsigned tmp_passwd_len;
226 unsigned username_len = strlen(username);
227 int ret;
228 ssize_t len, l;
229 size_t line_size;
230 struct stat st;
231
232 tmp_passwd_len = fpasswd_len + 5;
233 tmp_passwd = malloc(tmp_passwd_len);
234
235 snprintf(tmp_passwd, tmp_passwd_len, "%s.tmp", fpasswd);
236 if (stat(tmp_passwd, &st) != -1) {
237 fprintf(stderr, "file '%s' is locked.\n", fpasswd);
238 exit(1);
239 }
240
241 fd = fopen(fpasswd, "r");
242 if (fd == NULL) {
243 fprintf(stderr, "Cannot open '%s' for reading.\n", fpasswd);
244 exit(1);
245 }
246
247 fd2 = fopen(tmp_passwd, "w");
248 if (fd2 == NULL) {
249 fprintf(stderr, "Cannot open '%s' for writing.\n", tmp_passwd);
250 exit(1);
251 }
252
253 line = NULL;
254 while ((len = getline(&line, &line_size, fd)) > 0) {
255 p = strchr(line, ':');
256 if (p == NULL)
257 continue;
258
259 l = p-line;
260 if (l == username_len && strncmp(line, username, l) == 0) {
261 p = strchr(p+1, ':');
262 if (p == NULL)
263 continue;
264 p++;
265
266 if(*p != '!') {
267 l = p-line;
268 fwrite(line, 1, l, fd2);
269 fputc('!', fd2);
270 fwrite(p, 1, len-l, fd2);
271 } else {
272 fwrite(line, 1, len, fd2);
273 }
274 } else {
275 fwrite(line, 1, len, fd2);
276 }
277 }
278
279 free(line);
280 fclose(fd);
281 fclose(fd2);
282
283 ret = rename(tmp_passwd, fpasswd);
284 if (ret == -1) {
285 fprintf(stderr, "Cannot write to '%s'.\n", fpasswd);
286 exit(1);
287 }
288 free(tmp_passwd);
289 }
290
291 static void
unlock_user(const char * fpasswd,const char * username)292 unlock_user(const char *fpasswd, const char *username)
293 {
294 FILE * fd, *fd2;
295 char *tmp_passwd;
296 char *line, *p;
297 unsigned fpasswd_len = strlen(fpasswd);
298 unsigned tmp_passwd_len;
299 unsigned username_len = strlen(username);
300 int ret;
301 ssize_t len, l;
302 size_t line_size;
303 struct stat st;
304
305 tmp_passwd_len = fpasswd_len + 5;
306 tmp_passwd = malloc(tmp_passwd_len);
307
308 snprintf(tmp_passwd, tmp_passwd_len, "%s.tmp", fpasswd);
309 if (stat(tmp_passwd, &st) != -1) {
310 fprintf(stderr, "file '%s' is locked.\n", fpasswd);
311 exit(1);
312 }
313
314 fd = fopen(fpasswd, "r");
315 if (fd == NULL) {
316 fprintf(stderr, "Cannot open '%s' for reading.\n", fpasswd);
317 exit(1);
318 }
319
320 fd2 = fopen(tmp_passwd, "w");
321 if (fd2 == NULL) {
322 fprintf(stderr, "Cannot open '%s' for writing.\n", tmp_passwd);
323 exit(1);
324 }
325
326 line = NULL;
327 while ((len = getline(&line, &line_size, fd)) > 0) {
328 p = strchr(line, ':');
329 if (p == NULL)
330 continue;
331
332 l = p-line;
333 if (l == username_len && strncmp(line, username, l) == 0) {
334 p = strchr(p+1, ':');
335 if (p == NULL)
336 continue;
337 p++;
338
339 l = p-line;
340 fwrite(line, 1, l, fd2);
341
342 if (*p=='!') p++;
343 l = p-line;
344 fwrite(p, 1, len-l, fd2);
345 } else {
346 fwrite(line, 1, len, fd2);
347 }
348 }
349
350 free(line);
351 fclose(fd);
352 fclose(fd2);
353
354 ret = rename(tmp_passwd, fpasswd);
355 if (ret == -1) {
356 fprintf(stderr, "Cannot write to '%s'.\n", fpasswd);
357 exit(1);
358 }
359 free(tmp_passwd);
360 }
361
362 static const struct option long_options[] = {
363 {"passwd", 1, 0, 'c'},
364 {"groupname", 1, 0, 'g'},
365 {"delete", 0, 0, 'd'},
366 {"lock", 0, 0, 'l'},
367 {"unlock", 0, 0, 'u'},
368 {"help", 0, 0, 'h'},
369 {"version", 0, 0, 'v'},
370 {NULL, 0, 0, 0}
371 };
372
373 static
usage(void)374 void usage(void)
375 {
376 fprintf(stderr, "ocpasswd - OpenConnect server password utility\n");
377 fprintf(stderr, "Usage: ocpasswd [ -<flag> [<val>] | --<name>[{=| }<val>] ]... [username]\n");
378 fprintf(stderr, "\n");
379 fprintf(stderr, " -c, --passwd=file Password file\n");
380 fprintf(stderr, " -g, --groupname=str User's group name\n");
381 fprintf(stderr, " -d, --delete Delete user\n");
382 fprintf(stderr, " -l, --lock Lock user\n");
383 fprintf(stderr, " -u, --unlock Unlock user\n");
384 fprintf(stderr, " -v, --version output version information and exit\n");
385 fprintf(stderr, " -h, --help display extended usage information and exit\n");
386 fprintf(stderr, "\n");
387 fprintf(stderr, "Options are specified by doubled hyphens and their name or by a single\n");
388 fprintf(stderr, "hyphen and the flag character.\n\n");
389 fprintf(stderr, "This program is openconnect password (ocpasswd) utility. It allows the\n");
390 fprintf(stderr, "generation and handling of a 'plain' password file used by ocserv.\n\n");
391 fprintf(stderr, "Please send bug reports to: "PACKAGE_BUGREPORT"\n");
392 }
393
394 static
version(void)395 void version(void)
396 {
397 fprintf(stderr, "ocpasswd - "VERSION"\n");
398 fprintf(stderr, "Copyright (C) 2013-2017 Nikos Mavrogiannopoulos, all rights reserved.\n");
399 fprintf(stderr, "This is free software. It is licensed for use, modification and\n");
400 fprintf(stderr, "redistribution under the terms of the GNU General Public License,\n");
401 fprintf(stderr, "version 2 <http://gnu.org/licenses/gpl.html>\n\n");
402 fprintf(stderr, "Please send bug reports to: "PACKAGE_BUGREPORT"\n");
403 }
404
405 #define FLAG_DELETE 1
406 #define FLAG_LOCK (1<<1)
407 #define FLAG_UNLOCK (1<<2)
408
main(int argc,char ** argv)409 int main(int argc, char **argv)
410 {
411 int ret, c;
412 const char *username = NULL;
413 char *groupname = NULL, *fpasswd = NULL;
414 char* passwd = NULL;
415 unsigned free_passwd = 0;
416 size_t l, i;
417 unsigned flags = 0;
418
419 if ((ret = gnutls_global_init()) < 0) {
420 fprintf(stderr, "global_init: %s\n", gnutls_strerror(ret));
421 exit(1);
422 }
423
424 umask(066);
425
426 while (1) {
427 c = getopt_long(argc, argv, "c:g:dluvh", long_options, NULL);
428 if (c == -1)
429 break;
430
431 switch(c) {
432 case 'c':
433 if (fpasswd) {
434 fprintf(stderr, "-c option cannot be specified multiple time\n");
435 exit(1);
436 }
437 fpasswd = strdup(optarg);
438 break;
439 case 'g':
440 if (groupname) {
441 fprintf(stderr, "-g option cannot be specified multiple time\n");
442 exit(1);
443 }
444 groupname = strdup(optarg);
445 break;
446 case 'd':
447 if (flags) {
448 usage();
449 exit(1);
450 }
451 flags |= FLAG_DELETE;
452 break;
453 case 'u':
454 if (flags) {
455 usage();
456 exit(1);
457 }
458 flags |= FLAG_UNLOCK;
459 break;
460 case 'l':
461 if (flags) {
462 usage();
463 exit(1);
464 }
465 flags |= FLAG_LOCK;
466 break;
467 case 'h':
468 usage();
469 exit(0);
470 case 'v':
471 version();
472 exit(0);
473 }
474 }
475
476 if (optind < argc && argc-optind == 1) {
477 username = argv[optind++];
478 } else {
479 usage();
480 exit(1);
481 }
482
483 if (!groupname)
484 groupname = strdup("*");
485 if (!fpasswd)
486 fpasswd = strdup(DEFAULT_OCPASSWD);
487
488 if (flags & FLAG_LOCK) {
489 lock_user(fpasswd, username);
490 } else if (flags & FLAG_UNLOCK) {
491 unlock_user(fpasswd, username);
492 } else if (flags & FLAG_DELETE) {
493 delete_user(fpasswd, username);
494 } else { /* set password */
495
496 if (isatty(STDIN_FILENO)) {
497 char* p2;
498
499 passwd = getpass("Enter password: ");
500 if (passwd == NULL) {
501 fprintf(stderr, "Please specify a password\n");
502 exit(1);
503 }
504
505
506 p2 = strdup(passwd);
507 passwd = getpass("Re-enter password: ");
508 if (passwd == NULL) {
509 fprintf(stderr, "Please specify a password\n");
510 exit(1);
511 }
512
513 if (p2 == NULL || strcmp(passwd, p2) != 0) {
514 fprintf(stderr, "Passwords do not match\n");
515 exit(1);
516 }
517 free(p2);
518 } else {
519 passwd = NULL;
520 l = getline(&passwd, &i, stdin);
521 if (l <= 1) {
522 fprintf(stderr, "Please specify a password\n");
523 exit(1);
524 }
525
526 free_passwd = 1;
527 if (passwd[l-1] == '\n')
528 passwd[l-1] = 0;
529 }
530
531 crypt_int(fpasswd, username, groupname, passwd);
532 if (free_passwd)
533 free(passwd);
534 }
535
536 free(fpasswd);
537 free(groupname);
538 gnutls_global_deinit();
539 return 0;
540 }
541
542