Fix memory leaks from ircd_crypt and epoll support.
[ircu2.10.12-pk.git] / ircd / ircd_crypt_smd5.c
1 /*
2  * IRC - Internet Relay Chat, ircd/ircd_crypt_smd5.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  
20 /** 
21  * @file
22  * @brief Routines for Salted MD5 passwords
23  * @version $Id$
24  * 
25  * ircd_crypt_smd5 is largely taken from md5_crypt.c from the Linux PAM 
26  * source code.  it's been modified to fit in with ircu and some of the 
27  * undeeded code has been removed.  the source file md5_crypt.c has the 
28  * following licence, so if any of our opers or admins are in Denmark
29  * they better go buy them a drink ;) -- hikari
30  *
31  * ----------------------------------------------------------------------------
32  * "THE BEER-WARE LICENSE" (Revision 42):
33  * <phk@login.dknet.dk> wrote this file.  As long as you retain this notice you
34  * can do whatever you want with this stuff. If we meet some day, and you think
35  * this stuff is worth it, you can buy me a beer in return.   Poul-Henning Kamp
36  * ----------------------------------------------------------------------------
37  *
38  */
39 #include "config.h"
40 #include "ircd_crypt.h"
41 #include "ircd_crypt_smd5.h"
42 #include "ircd_md5.h"
43 #include "s_debug.h"
44 #include "ircd_alloc.h"
45
46 #include <assert.h>
47 #include <string.h>
48 #include <unistd.h>
49
50 static unsigned char itoa64[] = /* 0 ... 63 => ascii - 64 */
51 "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
52
53 /** Converts a binary value into a BASE64 encoded string.
54  * @param s Pointer to the output string
55  * @param v The unsigned long we're working on
56  * @param n The number of bytes we're working with
57  *  
58  * This is used to produce the normal MD5 hash everyone is familar with.  
59  * It takes the value v and converts n bytes of it it into an ASCII string in 
60  * 6-bit chunks, the resulting string is put at the address pointed to by s.
61  * 
62  */
63 static void to64(char *s, unsigned long v, int n)
64 {
65  while (--n >= 0) {
66   *s++ = itoa64[v & 0x3f];
67   v >>= 6;
68  }
69 }
70
71 /** Produces a Salted MD5 crypt of a password using the supplied salt
72  * @param key The password we're encrypting
73  * @param salt The salt we're using to encrypt it
74  * @return The Salted MD5 password of key and salt
75  * 
76  * Erm does exactly what the brief comment says.  If you think I'm writing a 
77  * description of how MD5 works, you have another thing comming.  Go and read
78  * Applied Cryptopgraphy by Bruce Schneier.  The only difference is we use a 
79  * salt at the begining of the password to perturb it so that the same password
80  * doesn't always produce the same hash.
81  * 
82  */ 
83 const char* ircd_crypt_smd5(const char* key, const char* salt)
84 {
85 const char *magic = "$1$";
86 static char passwd[120];
87 char *p;
88 const char *sp, *ep;
89 unsigned char final[16];
90 int sl, pl, i, j;
91 MD5_CTX ctx, ctx1;
92 unsigned long l;
93
94  assert(NULL != key);
95  assert(NULL != salt);
96
97  Debug((DEBUG_DEBUG, "ircd_crypt_smd5: key = %s", key));
98  Debug((DEBUG_DEBUG, "ircd_crypt_smd5: salt = %s", salt));
99
100  /* Refine the Salt first */
101  ep = sp = salt;
102
103  for (ep = sp; *ep && *ep != '$' && ep < (sp + 8); ep++)
104   continue;
105
106  /* get the length of the true salt */
107  sl = ep - sp;
108
109  MD5Init(&ctx);
110
111  /* The password first, since that is what is most unknown */
112  MD5Update(&ctx,(unsigned const char *)key,strlen(key));
113
114  /* Then our magic string */
115  MD5Update(&ctx,(unsigned const char *)magic,strlen(magic));
116
117  /* Then the raw salt */
118  MD5Update(&ctx,(unsigned const char *)sp,sl);
119
120  /* Then just as many characters of the MD5(key,salt,key) */
121  MD5Init(&ctx1);
122  MD5Update(&ctx1,(unsigned const char *)key,strlen(key));
123  MD5Update(&ctx1,(unsigned const char *)sp,sl);
124  MD5Update(&ctx1,(unsigned const char *)key,strlen(key));
125  MD5Final(final,&ctx1);
126  for (pl = strlen(key); pl > 0; pl -= 16)
127   MD5Update(&ctx,(unsigned const char *)final,pl>16 ? 16 : pl);
128
129  /* Don't leave anything around in vm they could use. */
130  memset(final, 0, sizeof final);
131
132  /* Then something really weird... */
133  for (j = 0, i = strlen(key); i; i >>= 1)
134   if (i & 1)
135    MD5Update(&ctx, (unsigned const char *)final+j, 1);
136   else
137    MD5Update(&ctx, (unsigned const char *)key+j, 1);
138
139  /* Now make the output string. */
140  memset(passwd, 0, 120);
141  strncpy(passwd, sp, sl);
142  strcat(passwd, "$");
143
144  MD5Final(final,&ctx);
145
146  /*
147   * and now, just to make sure things don't run too fast
148   * On a 60 Mhz Pentium this takes 34 msec, so you would
149   * need 30 seconds to build a 1000 entry dictionary...
150   */
151  for (i = 0; i < 1000; i++) {
152   MD5Init(&ctx1);
153
154   if (i & 1)
155    MD5Update(&ctx1,(unsigned const char *)key,strlen(key));
156   else
157    MD5Update(&ctx1,(unsigned const char *)final,16);
158
159   if (i % 3)
160    MD5Update(&ctx1,(unsigned const char *)sp,sl);
161
162   if (i % 7)
163    MD5Update(&ctx1,(unsigned const char *)key,strlen(key));
164
165   if (i & 1)
166    MD5Update(&ctx1,(unsigned const char *)final,16);
167   else
168    MD5Update(&ctx1,(unsigned const char *)key,strlen(key));
169
170   MD5Final(final,&ctx1);
171  }
172
173  p = passwd + strlen(passwd);
174
175  Debug((DEBUG_DEBUG, "passwd = %s", passwd));
176
177  /* Turn the encrypted binary data into a BASE64 encoded string we can read
178   * and display -- hikari */
179  l = (final[0] << 16) | (final[6] << 8) | final[12];
180  to64(p, l, 4);
181  p += 4;
182  l = (final[1] << 16) | (final[7] << 8) | final[13];
183  to64(p, l, 4);
184  p += 4;
185  l = (final[2] << 16) | (final[8] << 8) | final[14];
186  to64(p, l, 4);
187  p += 4;
188  l = (final[3] << 16) | (final[9] << 8) | final[15];
189  to64(p, l, 4);
190  p += 4;
191  l = (final[4] << 16) | (final[10] << 8) | final[5];
192  to64(p, l, 4);
193  p += 4;
194  l = final[11];
195  to64(p, l, 2);
196  p += 2;
197  *p = '\0';
198
199  /* Don't leave anything around in vm they could use. */
200  memset(final, 0, sizeof final);
201
202 return passwd;
203 }
204
205 /* end borrowed code */
206
207 /** Register ourself with the list of crypt mechanisms 
208  * Registers the SMD5 mechanism in the list of available crypt mechanisms.  When 
209  * we're modular this will be the entry function for the module.
210  * 
211  */
212 void ircd_register_crypt_smd5(void)
213 {
214 crypt_mech_t* crypt_mech;
215
216  if ((crypt_mech = (crypt_mech_t*)MyMalloc(sizeof(crypt_mech_t))) == NULL)
217  {
218   Debug((DEBUG_MALLOC, "Could not allocate space for crypt_smd5"));
219   return;
220  }
221
222  crypt_mech->mechname = "smd5";
223  crypt_mech->shortname = "crypt_smd5";
224  crypt_mech->description = "Salted MD5 password hash mechanism.";
225  crypt_mech->crypt_function = &ircd_crypt_smd5;
226  crypt_mech->crypt_token = "$SMD5$";
227  crypt_mech->crypt_token_size = 6 ;
228
229  ircd_crypt_register_mech(crypt_mech);
230  
231 return;
232 }