Fixes to improve portability (especially to OS X, Solaris, OpenBSD).
[ircu2.10.12-pk.git] / ircd / umkpasswd.c
1 /*
2  * IRC - Internet Relay Chat, ircd/umkpasswd.c
3  * Copyright (C) 2002 hikari
4  *
5  * This program is free software; you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation; either version 1, or (at your option)
8  * any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program; if not, write to the Free Software
17  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18  *
19  * $Id$
20 */
21 #include "config.h"
22 #include <unistd.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <errno.h>
26 #include <stdlib.h>
27 #include <string.h>
28 #include <time.h>
29 #include <assert.h>
30
31 /* ircu headers */
32 #include "ircd_alloc.h"
33 #include "ircd_string.h"
34 #include "umkpasswd.h"
35 #include "s_debug.h"
36 #include "ircd_md5.h"
37
38 /* crypto mech headers */
39 #include "ircd_crypt.h"
40 #include "ircd_crypt_smd5.h"
41 #include "ircd_crypt_native.h"
42 #include "ircd_crypt_plain.h"
43
44 /* bleah, evil globals */
45 umkpasswd_conf_t* umkpasswd_conf;
46 crypt_mechs_t* crypt_mechs_root;
47
48 void copyright(void)
49 {
50   fprintf(stderr, "umkpasswd - Copyright (c) 2002 hikari\n");
51 return;
52 }
53
54 void show_help(void)
55 {
56 #ifdef DEBUGMODE
57  char *debughelp = "[-d <level>] ";
58 #else
59  char *debughelp = "";
60 #endif
61
62  copyright();
63  /*fprintf(stderr, "umkpasswd [-l] [[[-a]||[-u]] <username>] [-y <class>] %s[-c <file>] -m <mech> [password]\n\n", debughelp);*/
64  fprintf(stderr, "umkpasswd [-l] %s-m <mech> [password]\n\n", debughelp);
65  fprintf(stderr, "  -l            List mechanisms available.\n");
66 #if 0
67  fprintf(stderr, "  -a <user>     Add user to conf file.\n");
68  fprintf(stderr, "  -u <user>     Update user's password field.\n");
69  fprintf(stderr, "  -y <class>    Class to place oper in.\n");
70 #endif
71  fprintf(stderr, "  -m <mech>     Mechanism to use [MANDATORY].\n");
72 #ifdef DEBUGMODE
73  fprintf(stderr, "  -d <level>    Debug level to run at.\n");
74 #endif
75 /*
76  fprintf(stderr, "  -c <file>     Conf file to use, default is DPATH/CPATH.\n\n");
77 */
78 return;
79 }
80
81 /* our implementation of debug() */
82 void debug(int level, const char *form, ...)
83 {
84 va_list vl;
85 int err = errno;
86
87   if (level <= (umkpasswd_conf->debuglevel))
88   {
89     va_start(vl, form);
90     vfprintf(stderr, form, vl);
91     fprintf(stderr, "\n");
92     va_end(vl);
93   }
94   errno = err;
95 }
96
97 /* quick and dirty salt generator */
98 char *make_salt(const char *salts)
99 {
100 char *tmp = NULL;
101 long int n = 0;
102
103  /* try and get around them running this time after time in quick sucession */
104  sleep(1);
105  srandom((unsigned int)time(NULL));
106
107  if((tmp = calloc(3, sizeof(char))) != NULL)
108  {
109   /* can't optimize this much more than just doing it twice */
110   n = ((float)(strlen(salts))*random()/(RAND_MAX+1.0));
111   memcpy(tmp, (salts+n), 1);
112   sleep(2);
113   n = ((float)(strlen(salts))*random()/(RAND_MAX+1.0));
114   memcpy((tmp+1), (salts+n), 1);
115
116   Debug((DEBUG_DEBUG, "salts = %s", salts));
117   Debug((DEBUG_DEBUG, "strlen(salts) = %d", strlen(salts)));
118  }
119
120 return tmp;
121 }
122
123 /* our implemenation of ircd_crypt_register_mech() */
124 int ircd_crypt_register_mech(crypt_mech_t* mechanism)
125 {
126 crypt_mechs_t* crypt_mech;
127
128  Debug((DEBUG_INFO, "ircd_crypt_register_mech: resistering mechanism: %s", mechanism->shortname));
129
130  /* try to allocate some memory for the new mechanism */
131  if ((crypt_mech = (crypt_mechs_t*)MyMalloc(sizeof(crypt_mechs_t))) == NULL)
132  {
133   /* aww poot, we couldn't get any memory, scream a little then back out */
134   Debug((DEBUG_MALLOC, "ircd_crypt_register_mech: could not allocate memory for %s", mechanism->shortname));
135   return -1;
136  }
137
138  /* ok, we have memory, initialise it */
139  memset(crypt_mech, 0, sizeof(crypt_mechs_t));
140
141  /* assign the data */
142  crypt_mech->mech = mechanism;
143  crypt_mech->next = crypt_mech->prev = NULL;
144
145  /* first of all, is there anything there already? */
146  if(crypt_mechs_root->next == NULL)
147  {
148   /* nope, just add ourself */
149   crypt_mechs_root->next = crypt_mechs_root->prev = crypt_mech;
150  } else {
151   /* nice and simple, put ourself at the end */
152   crypt_mech->prev = crypt_mechs_root->prev;
153   crypt_mech->next = NULL;
154   crypt_mechs_root->prev = crypt_mech->prev->next = crypt_mech;
155  }
156
157  /* we're done */
158  Debug((DEBUG_INFO, "ircd_crypt_register_mech: resistered mechanism: %s, crypt_function is at 0x%X.", crypt_mech->mech->shortname, &crypt_mech->mech->crypt_function));
159  Debug((DEBUG_INFO, "ircd_crypt_register_mech: %s: %s", crypt_mech->mech->shortname, crypt_mech->mech->description));
160
161 return 0;
162 }
163
164 char *basename_into(char *tmp, char *target)
165 {
166   unsigned int len, ii;
167
168   len = strlen(tmp);
169   for (ii = len; ii > 0; )
170     if (tmp[--ii] == '/')
171       break;
172   if (ii < len - 1)
173     return tmp + ii + (tmp[ii] == '/');
174   else if (tmp[ii] != '/')
175     return tmp;
176   else if (ii == 0)
177     return tmp;
178   else
179   {
180     while (ii > 0)
181       if (tmp[--ii] == '/')
182          break;
183     if (tmp[ii] == '/')
184         ii++;
185     for (len = 0; tmp[ii] != '/'; )
186       target[len++] = tmp[ii++];
187     target[len] = '\0';
188     return target;
189   }
190 }
191
192 void sum(char* tmp)
193 {
194 FILE* file;
195 MD5_CTX context;
196 int len;
197 unsigned char buffer[1024], digest[16];
198
199  if (NULL == (file = fopen(tmp, "r")))
200   exit(0);
201  MD5Name(MD5Init)(&context);
202  while ((len = fread (buffer, 1, 1024, file)))
203   MD5Name(MD5Update)(&context, buffer, len);
204  MD5Name(MD5Final)(digest, &context);
205  fclose(file);
206
207  printf("%s: ", basename_into(tmp, (char*)buffer));
208  for (len = 0; len < 16; len++)
209   printf ("%02x", digest[len]);
210  printf("\n");
211  exit(0);
212 }
213
214 /* dump the loaded mechs list */
215 void show_mechs(void)
216 {
217 crypt_mechs_t* mechs;
218
219  copyright();
220  printf("\nAvailable mechanisms:\n");
221
222  if(crypt_mechs_root == NULL)
223   return;
224
225  mechs = crypt_mechs_root->next;
226
227  for(;;)
228  {
229   if(mechs == NULL)
230    return;
231
232   printf(" %s\t\t%s\n", mechs->mech->mechname, mechs->mech->description);
233
234   mechs = mechs->next;
235  }
236 }
237
238 /* load in the mech "modules" */
239 void load_mechs(void)
240 {
241  /* we need these loaded by hand for now */
242
243  ircd_register_crypt_native();
244  ircd_register_crypt_smd5();
245  ircd_register_crypt_plain(); /* yes I know it's slightly pointless */
246
247 return;
248 }
249
250 crypt_mechs_t* hunt_mech(const char* mechname)
251 {
252 crypt_mechs_t* mech;
253
254  assert(NULL != mechname);
255
256  if(crypt_mechs_root == NULL)
257   return NULL;
258
259  mech = crypt_mechs_root->next;
260
261  for(;;)
262  {
263   if(mech == NULL)
264    return NULL;
265
266   if(0 == (ircd_strcmp(mech->mech->mechname, mechname)))
267    return mech;
268
269   mech = mech->next;
270  }
271 }
272
273 char* crypt_pass(const char* pw, const char* mech)
274 {
275 crypt_mechs_t* crypt_mech;
276 char* salt, *untagged, *tagged;
277
278  assert(NULL != pw);
279  assert(NULL != mech);
280
281  Debug((DEBUG_DEBUG, "pw = %s\n", pw));
282  Debug((DEBUG_DEBUG, "mech = %s\n", mech));
283
284  if (NULL == (crypt_mech = hunt_mech(mech)))
285  {
286   printf("Unable to find mechanism %s\n", mech);
287   return NULL;
288  }
289
290  salt = make_salt(default_salts);
291
292  untagged = (char *)CryptFunc(crypt_mech->mech)(pw, salt);
293  tagged = (char *)MyMalloc(strlen(salt)+CryptTokSize(crypt_mech->mech)+1);
294  memset(tagged, 0, strlen(untagged)+CryptTokSize(crypt_mech->mech)+1);
295  strncpy(tagged, CryptTok(crypt_mech->mech), CryptTokSize(crypt_mech->mech));
296  strncpy(tagged+CryptTokSize(crypt_mech->mech), untagged, strlen(untagged));
297
298 return tagged;
299 }
300
301 char* parse_arguments(int argc, char **argv)
302 {
303 int len = 0, c = 0;
304 const char* options = "a:d:lm:u:y:5:";
305
306  umkpasswd_conf = (umkpasswd_conf_t*)MyMalloc(sizeof(umkpasswd_conf_t));
307
308  umkpasswd_conf->flags = 0;
309  umkpasswd_conf->debuglevel = 0;
310  umkpasswd_conf->operclass = 0;
311  umkpasswd_conf->user = NULL;
312  umkpasswd_conf->mech = NULL;
313
314
315  len = strlen(DPATH) + strlen(CPATH) + 2;
316  umkpasswd_conf->conf = (char *)MyMalloc(len*sizeof(char));
317  memset(umkpasswd_conf->conf, 0, len*sizeof(char));
318  ircd_strncpy(umkpasswd_conf->conf, DPATH, strlen(DPATH));
319  *((umkpasswd_conf->conf) + strlen(DPATH)) = '/';
320  ircd_strncpy((umkpasswd_conf->conf) + strlen(DPATH) + 1, CPATH, strlen(CPATH));
321
322  len = 0;
323
324  while ((EOF != (c = getopt(argc, argv, options))) && !len)
325  {
326   switch (c)
327   {
328    case '5':
329     sum(optarg);
330    break;
331
332    case 'y':
333     umkpasswd_conf->operclass = atoi(optarg);
334     if (umkpasswd_conf->operclass < 0)
335      umkpasswd_conf->operclass = 0;
336    break;
337
338    case 'u':
339     if(umkpasswd_conf->flags && ACT_UPDOPER)
340     {
341      fprintf(stderr, "-a and -u are mutually exclussive.  Use either or neither.\n");
342      abort(); /* b0rk b0rk b0rk */
343     }
344
345     umkpasswd_conf->flags |= ACT_UPDOPER;
346     umkpasswd_conf->user = optarg;
347    break;
348
349    case 'm':
350     umkpasswd_conf->mech = optarg;
351    break;
352
353    case 'l':
354     load_mechs();
355     show_mechs();
356     exit(0);
357    break;
358
359    case 'd':
360     umkpasswd_conf->debuglevel = atoi(optarg);
361     if (umkpasswd_conf->debuglevel < 0)
362      umkpasswd_conf->debuglevel = 0;
363    break;
364
365    case 'c':
366     umkpasswd_conf->conf = optarg;
367    break;
368
369    case 'a':
370     if(umkpasswd_conf->flags && ACT_UPDOPER) 
371     {
372      fprintf(stderr, "-a and -u are mutually exclussive.  Use either or neither.\n");
373      abort(); /* b0rk b0rk b0rk */
374     }
375
376     umkpasswd_conf->flags |= ACT_ADDOPER;
377     umkpasswd_conf->user = optarg;
378    break;
379
380    default:
381     /* unknown option - spit out syntax and b0rk */
382     show_help();
383     abort();
384    break;
385   }
386  }
387
388  Debug((DEBUG_DEBUG, "flags = %d", umkpasswd_conf->flags));
389  Debug((DEBUG_DEBUG, "operclass = %d", umkpasswd_conf->operclass));
390  Debug((DEBUG_DEBUG, "debug = %d", umkpasswd_conf->debuglevel));
391
392  if (NULL != umkpasswd_conf->mech)
393   Debug((DEBUG_DEBUG, "mech = %s", umkpasswd_conf->mech));
394  else
395   Debug((DEBUG_DEBUG, "mech is unset"));
396
397  if (NULL != umkpasswd_conf->conf)
398   Debug((DEBUG_DEBUG, "conf = %s", umkpasswd_conf->conf));
399  else
400   Debug((DEBUG_DEBUG, "conf is unset"));
401
402  if (NULL != umkpasswd_conf->user)
403   Debug((DEBUG_DEBUG, "user = %s", umkpasswd_conf->user));
404  else
405   Debug((DEBUG_DEBUG, "user is unset"));
406
407 /* anything left over should be password */
408 return argv[optind];
409 }
410
411 int main(int argc, char **argv)
412 {
413 char* pw, *crypted_pw;
414
415  crypt_mechs_root = (crypt_mechs_t*)MyMalloc(sizeof(crypt_mechs_t));
416  crypt_mechs_root->mech = NULL;
417  crypt_mechs_root->next = crypt_mechs_root->prev = NULL;
418
419  if (argc < 2)
420  {
421   show_help();
422   exit(0);
423  }
424
425  pw = parse_arguments(argc, argv);
426  load_mechs();
427
428  if (NULL == umkpasswd_conf->mech)
429  {
430   fprintf(stderr, "No mechanism specified.\n");
431   abort();
432  }
433
434  if (NULL == pw)
435  {
436   pw = getpass("Password: ");
437  }
438  crypted_pw = crypt_pass(pw, umkpasswd_conf->mech);
439
440  printf("Crypted Pass: %s\n", crypted_pw);
441  memset(pw, 0, strlen(pw));
442
443 return 0;
444 }