UNPKG

11.1 kBtext/x-cView Raw
1/*
2Copyright (c) 2012,2013 Roger Light <roger@atchoo.org>
3All rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are met:
7
81. Redistributions of source code must retain the above copyright notice,
9 this list of conditions and the following disclaimer.
102. Redistributions in binary form must reproduce the above copyright
11 notice, this list of conditions and the following disclaimer in the
12 documentation and/or other materials provided with the distribution.
133. Neither the name of mosquitto nor the names of its
14 contributors may be used to endorse or promote products derived from
15 this software without specific prior written permission.
16
17THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27POSSIBILITY OF SUCH DAMAGE.
28*/
29
30
31#include <errno.h>
32#include <openssl/evp.h>
33#include <openssl/rand.h>
34#include <openssl/buffer.h>
35#include <signal.h>
36#include <stdio.h>
37#include <stdlib.h>
38#include <string.h>
39#ifdef WIN32
40# include <process.h>
41# ifndef __cplusplus
42# define bool char
43# define true 1
44# define false 0
45# endif
46# define snprintf sprintf_s
47# include <io.h>
48#else
49# include <stdbool.h>
50# include <unistd.h>
51# include <termios.h>
52#endif
53
54#define MAX_BUFFER_LEN 1024
55#define SALT_LEN 12
56
57int base64_encode(unsigned char *in, unsigned int in_len, char **encoded)
58{
59 BIO *bmem, *b64;
60 BUF_MEM *bptr;
61
62 b64 = BIO_new(BIO_f_base64());
63 BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
64 bmem = BIO_new(BIO_s_mem());
65 b64 = BIO_push(b64, bmem);
66 BIO_write(b64, in, in_len);
67 if(BIO_flush(b64) != 1){
68 BIO_free_all(b64);
69 return 1;
70 }
71 BIO_get_mem_ptr(b64, &bptr);
72 *encoded = calloc(bptr->length+1, 1);
73 if(!(*encoded)){
74 BIO_free_all(b64);
75 return 1;
76 }
77 memcpy(*encoded, bptr->data, bptr->length);
78 (*encoded)[bptr->length] = '\0';
79 BIO_free_all(b64);
80
81 return 0;
82}
83
84
85void print_usage(void)
86{
87 printf("mosquitto_passwd is a tool for managing password files for mosquitto.\n\n");
88 printf("Usage: mosquitto_passwd [-c | -D] passwordfile username\n");
89 printf(" mosquitto_passwd -U passwordfile\n");
90 printf(" -c : create a new password file. This will overwrite existing files.\n");
91 printf(" -D : delete the username rather than adding/updating its password.\n");
92 printf(" -U : update a plain text password file to use hashed passwords.\n");
93 printf("\nSee http://mosquitto.org/ for more information.\n\n");
94}
95
96int output_new_password(FILE *fptr, const char *username, const char *password)
97{
98 int rc;
99 unsigned char salt[SALT_LEN];
100 char *salt64 = NULL, *hash64 = NULL;
101 unsigned char hash[EVP_MAX_MD_SIZE];
102 unsigned int hash_len;
103 const EVP_MD *digest;
104 EVP_MD_CTX context;
105
106 rc = RAND_bytes(salt, SALT_LEN);
107 if(!rc){
108 fprintf(stderr, "Error: Insufficient entropy available to perform password generation.\n");
109 return 1;
110 }
111
112 rc = base64_encode(salt, SALT_LEN, &salt64);
113 if(rc){
114 if(salt64) free(salt64);
115 fprintf(stderr, "Error: Unable to encode salt.\n");
116 return 1;
117 }
118
119
120 digest = EVP_get_digestbyname("sha512");
121 if(!digest){
122 fprintf(stderr, "Error: Unable to create openssl digest.\n");
123 return 1;
124 }
125
126 EVP_MD_CTX_init(&context);
127 EVP_DigestInit_ex(&context, digest, NULL);
128 EVP_DigestUpdate(&context, password, strlen(password));
129 EVP_DigestUpdate(&context, salt, SALT_LEN);
130 EVP_DigestFinal_ex(&context, hash, &hash_len);
131 EVP_MD_CTX_cleanup(&context);
132
133 rc = base64_encode(hash, hash_len, &hash64);
134 if(rc){
135 if(salt64) free(salt64);
136 if(hash64) free(hash64);
137 fprintf(stderr, "Error: Unable to encode hash.\n");
138 return 1;
139 }
140
141 fprintf(fptr, "%s:$6$%s$%s\n", username, salt64, hash64);
142 free(salt64);
143 free(hash64);
144
145 return 0;
146}
147
148int delete_pwuser(FILE *fptr, FILE *ftmp, const char *username)
149{
150 char buf[MAX_BUFFER_LEN];
151 char lbuf[MAX_BUFFER_LEN], *token;
152 bool found = false;
153
154 while(!feof(fptr) && fgets(buf, MAX_BUFFER_LEN, fptr)){
155 memcpy(lbuf, buf, MAX_BUFFER_LEN);
156 token = strtok(lbuf, ":");
157 if(strcmp(username, token)){
158 fprintf(ftmp, "%s", buf);
159 }else{
160 found = true;
161 }
162 }
163 if(!found){
164 fprintf(stderr, "Warning: User %s not found in password file.\n", username);
165 }
166 return 0;
167}
168
169int update_file(FILE *fptr, FILE *ftmp)
170{
171 char buf[MAX_BUFFER_LEN];
172 char lbuf[MAX_BUFFER_LEN];
173 char *username, *password;
174 int rc;
175 int len;
176
177 while(!feof(fptr) && fgets(buf, MAX_BUFFER_LEN, fptr)){
178 memcpy(lbuf, buf, MAX_BUFFER_LEN);
179 username = strtok(lbuf, ":");
180 password = strtok(NULL, ":");
181 if(password){
182 len = strlen(password);
183 while(len && (password[len-1] == '\n' || password[len-1] == '\r')){
184 password[len-1] = '\0';
185 len = strlen(password);
186 }
187 rc = output_new_password(ftmp, username, password);
188 if(rc) return rc;
189 }else{
190 fprintf(ftmp, "%s", username);
191 }
192 }
193 return 0;
194}
195
196int update_pwuser(FILE *fptr, FILE *ftmp, const char *username, const char *password)
197{
198 char buf[MAX_BUFFER_LEN];
199 char lbuf[MAX_BUFFER_LEN], *token;
200 bool found = false;
201 int rc = 1;
202
203 while(!feof(fptr) && fgets(buf, MAX_BUFFER_LEN, fptr)){
204 memcpy(lbuf, buf, MAX_BUFFER_LEN);
205 token = strtok(lbuf, ":");
206 if(strcmp(username, token)){
207 fprintf(ftmp, "%s", buf);
208 }else{
209 rc = output_new_password(ftmp, username, password);
210 found = true;
211 }
212 }
213 if(found){
214 return rc;
215 }else{
216 return output_new_password(ftmp, username, password);
217 }
218}
219
220int gets_quiet(char *s, int len)
221{
222#ifdef WIN32
223 HANDLE h;
224 DWORD con_orig, con_quiet;
225 DWORD read_len = 0;
226
227 memset(s, 0, len);
228 h = GetStdHandle(STD_INPUT_HANDLE);
229 GetConsoleMode(h, &con_orig);
230 con_quiet &= ~ENABLE_ECHO_INPUT;
231 con_quiet |= ENABLE_LINE_INPUT;
232 SetConsoleMode(h, con_quiet);
233 if(!ReadConsole(h, s, len, &read_len, NULL)){
234 SetConsoleMode(h, con_orig);
235 return 1;
236 }
237 while(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){
238 s[strlen(s)-1] = 0;
239 }
240 if(strlen(s) == 0){
241 return 1;
242 }
243 SetConsoleMode(h, con_orig);
244
245 return 0;
246#else
247 struct termios ts_quiet, ts_orig;
248 char *rs;
249
250 memset(s, 0, len);
251 tcgetattr(0, &ts_orig);
252 ts_quiet = ts_orig;
253 ts_quiet.c_lflag &= ~(ECHO | ICANON);
254 tcsetattr(0, TCSANOW, &ts_quiet);
255
256 rs = fgets(s, len, stdin);
257 tcsetattr(0, TCSANOW, &ts_orig);
258
259 if(!rs){
260 return 1;
261 }else{
262 while(s[strlen(s)-1] == 10 || s[strlen(s)-1] == 13){
263 s[strlen(s)-1] = 0;
264 }
265 if(strlen(s) == 0){
266 return 1;
267 }
268 }
269 return 0;
270#endif
271}
272
273int get_password(char *password, int len)
274{
275 char pw1[MAX_BUFFER_LEN], pw2[MAX_BUFFER_LEN];
276
277 printf("Password: ");
278 if(gets_quiet(pw1, MAX_BUFFER_LEN)){
279 fprintf(stderr, "Error: Empty password.\n");
280 return 1;
281 }
282 printf("\n");
283
284 printf("Reenter password: ");
285 if(gets_quiet(pw2, MAX_BUFFER_LEN)){
286 fprintf(stderr, "Error: Empty password.\n");
287 return 1;
288 }
289 printf("\n");
290
291 if(strcmp(pw1, pw2)){
292 fprintf(stderr, "Error: Passwords do not match.\n");
293 return 1;
294 }
295
296 strncpy(password, pw1, len);
297 return 0;
298}
299
300int copy_contents(FILE *src, FILE *dest)
301{
302 char buf[MAX_BUFFER_LEN];
303 int len;
304
305 rewind(src);
306 rewind(dest);
307
308#ifdef WIN32
309 _chsize(fileno(dest), 0);
310#else
311 if(ftruncate(fileno(dest), 0)) return 1;
312#endif
313
314 while(!feof(src)){
315 len = fread(buf, 1, MAX_BUFFER_LEN, src);
316 if(len > 0){
317 if(fwrite(buf, 1, len, dest) != len){
318 return 1;
319 }
320 }else{
321 return !feof(src);
322 }
323 }
324 return 0;
325}
326
327int create_backup(const char *backup_file, FILE *fptr)
328{
329 FILE *fbackup;
330
331 fbackup = fopen(backup_file, "wt");
332 if(!fbackup){
333 fprintf(stderr, "Error creating backup password file \"%s\", not continuing.\n", backup_file);
334 return 1;
335 }
336 if(copy_contents(fptr, fbackup)){
337 fprintf(stderr, "Error copying data to backup password file \"%s\", not continuing.\n", backup_file);
338 fclose(fbackup);
339 return 1;
340 }
341 fclose(fbackup);
342 rewind(fptr);
343 return 0;
344}
345void handle_sigint(int signal)
346{
347#ifndef WIN32
348 struct termios ts;
349
350 tcgetattr(0, &ts);
351 ts.c_lflag |= ECHO | ICANON;
352 tcsetattr(0, TCSANOW, &ts);
353#endif
354 exit(0);
355}
356
357int main(int argc, char *argv[])
358{
359 char *password_file = NULL;
360 char *username = NULL;
361 bool create_new = false;
362 bool delete_user = false;
363 FILE *fptr, *ftmp;
364 char password[MAX_BUFFER_LEN];
365 int rc;
366 bool do_update_file = false;
367 char *backup_file;
368
369 signal(SIGINT, handle_sigint);
370 signal(SIGTERM, handle_sigint);
371
372 OpenSSL_add_all_digests();
373
374 if(argc == 4){
375 if(!strcmp(argv[1], "-c")){
376 create_new = true;
377 }else if(!strcmp(argv[1], "-D")){
378 delete_user = true;
379 }
380 password_file = argv[2];
381 username = argv[3];
382 }else if(argc == 3){
383 if(!strcmp(argv[1], "-U")){
384 do_update_file = true;
385 password_file = argv[2];
386 }else{
387 password_file = argv[1];
388 username = argv[2];
389 }
390 }else{
391 print_usage();
392 return 1;
393 }
394
395 if(create_new){
396 rc = get_password(password, 1024);
397 if(rc) return rc;
398 fptr = fopen(password_file, "wt");
399 if(!fptr){
400 fprintf(stderr, "Error: Unable to open file %s for writing. %s.\n", password_file, strerror(errno));
401 return 1;
402 }
403 rc = output_new_password(fptr, username, password);
404 fclose(fptr);
405 return rc;
406 }else{
407 fptr = fopen(password_file, "r+t");
408 if(!fptr){
409 fprintf(stderr, "Error: Unable to open password file %s. %s.\n", password_file, strerror(errno));
410 return 1;
411 }
412
413 backup_file = malloc(strlen(password_file)+5);
414 snprintf(backup_file, strlen(password_file)+5, "%s.tmp", password_file);
415
416 if(create_backup(backup_file, fptr)){
417 fclose(fptr);
418 free(backup_file);
419 return 1;
420 }
421
422 ftmp = tmpfile();
423 if(!ftmp){
424 fprintf(stderr, "Error: Unable to open temporary file. %s.\n", strerror(errno));
425 fclose(fptr);
426 free(backup_file);
427 return 1;
428 }
429 if(delete_user){
430 rc = delete_pwuser(fptr, ftmp, username);
431 }else if(do_update_file){
432 rc = update_file(fptr, ftmp);
433 }else{
434 rc = get_password(password, 1024);
435 if(rc){
436 fclose(fptr);
437 fclose(ftmp);
438 unlink(backup_file);
439 free(backup_file);
440 return rc;
441 }
442 /* Update password for individual user */
443 rc = update_pwuser(fptr, ftmp, username, password);
444 }
445 if(rc){
446 fclose(fptr);
447 fclose(ftmp);
448 unlink(backup_file);
449 free(backup_file);
450 return rc;
451 }
452
453 if(copy_contents(ftmp, fptr)){
454 fclose(fptr);
455 fclose(ftmp);
456 fprintf(stderr, "Error occurred updating password file.\n");
457 fprintf(stderr, "Password file may be corrupt, check the backup file: %s.\n", backup_file);
458 free(backup_file);
459 return 1;
460 }
461 fclose(fptr);
462 fclose(ftmp);
463
464 /* Everything was ok so backup no longer needed. May contain old
465 * passwords so shouldn't be kept around. */
466 unlink(backup_file);
467 free(backup_file);
468 }
469
470 return 0;
471}