Fix memory leaks from ircd_crypt and epoll support.
[ircu2.10.12-pk.git] / ircd / ircd_crypt.c
1 /*
2  * IRC - Internet Relay Chat, ircd/ircd_crypt.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 /**
22  * @file
23  * @brief Core password encryption routines.
24  * @version $Id$
25  * 
26  * This is a new look crypto API for ircu, it can handle different
27  * password formats by the grace of magic tokens at the begining of the 
28  * password e.g. $SMD5 for Salted MD5, $CRYPT for native crypt(), etc.
29  *
30  * Currently crypt routines are implemented for: the native crypt() 
31  * function, Salted MD5 and a plain text mechanism which should only
32  * be used for testing.  I intend to add Blowish, 3DES and possibly
33  * SHA1 support as well at some point, but I'll need to check the
34  * possible problems that'll cause with stupid crypto laws.
35  *
36  * It's also designed to be "ready" for the modularisation of ircu, so 
37  * someone get round to doing it, because I'm not doing it ;)
38  * 
39  * The plan for Stage B is to semi-modularise the authentication
40  * mechanism to allow authentication against some other sources than 
41  * the conf file (whatever takes someones fancy, kerberos, ldap, sql, etc).
42  *
43  *                   -- blessed be, hikari.
44  */
45
46 #include "config.h"
47 #include "ircd_crypt.h"
48 #include "ircd_alloc.h"
49 #include "ircd_features.h"
50 #include "ircd_string.h"
51 #include "s_debug.h"
52
53 /* while we're not modular, we need their init functions */
54 #include "ircd_crypt_native.h"
55 #include "ircd_crypt_plain.h"
56 #include "ircd_crypt_smd5.h"
57
58 #include <assert.h>
59 #include <unistd.h>
60 #include <string.h>
61
62 /* evil global */
63 crypt_mechs_t* crypt_mechs_root;
64
65 /** Add a crypt mechanism to the list 
66  * @param mechanism Pointer to the mechanism details struct
67  * @return 0 on success, anything else on fail.
68  * 
69  * This routine registers a new crypt mechanism in the loaded mechanisms list, 
70  * making it availabe for comparing passwords.
71 */
72 int ircd_crypt_register_mech(crypt_mech_t* mechanism)
73 {
74 crypt_mechs_t* crypt_mech;
75
76  Debug((DEBUG_INFO, "ircd_crypt_register_mech: resistering mechanism: %s", mechanism->shortname));
77
78  /* try to allocate some memory for the new mechanism */
79  if ((crypt_mech = (crypt_mechs_t*)MyMalloc(sizeof(crypt_mechs_t))) == NULL)
80  {
81   /* aww poot, we couldn't get any memory, scream a little then back out */
82   Debug((DEBUG_MALLOC, "ircd_crypt_register_mech: could not allocate memory for %s", mechanism->shortname));
83   return -1;
84  }
85
86  /* ok, we have memory, initialise it */
87  memset(crypt_mech, 0, sizeof(crypt_mechs_t));
88
89  /* assign the data */
90  crypt_mech->mech = mechanism;
91  crypt_mech->next = crypt_mech->prev = NULL;
92
93  /* first of all, is there anything there already? */
94  if(crypt_mechs_root->next == NULL)
95  {
96   /* nope, just add ourself */
97   crypt_mechs_root->next = crypt_mechs_root->prev = crypt_mech;
98  } else {
99   /* nice and simple, put ourself at the end */
100   crypt_mech->prev = crypt_mechs_root->prev;
101   crypt_mech->next = NULL;
102   crypt_mechs_root->prev = crypt_mech->prev->next = crypt_mech;
103  }
104   
105  /* we're done */
106  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));
107  Debug((DEBUG_INFO, "ircd_crypt_register_mech: %s: %s", crypt_mech->mech->shortname, crypt_mech->mech->description));
108  return 0;
109 }
110
111 /** Remove a crypt mechanism from the list 
112  * @param mechanism Pointer to the mechanism we want to remove
113  * @return 0 on success, anything else on fail.
114 */
115 int ircd_crypt_unregister_mech(crypt_mech_t* mechanism)
116 {
117
118 return 0;
119 }
120
121 /** Wrapper for generating a hashed password passed on the supplied password
122  * @param key Pointer to the password we want crypted
123  * @param salt Pointer to the password we're comparing to (for the salt)
124  * @return Pointer to the generated password (must be MyFree()'d).
125  *
126  * This is a wrapper function which attempts to establish the password
127  * format and funnel it off to the correct mechanism handler function.  The
128  * returned password is compared in the oper_password_match() routine.
129 */
130 char* ircd_crypt(const char* key, const char* salt)
131 {
132 char *hashed_pass = NULL;
133 const char *temp_hashed_pass, *mysalt;
134 crypt_mechs_t* crypt_mech;
135
136  assert(NULL != key);
137  assert(NULL != salt);
138
139  Debug((DEBUG_DEBUG, "ircd_crypt: key is %s", key));
140  Debug((DEBUG_DEBUG, "ircd_crypt: salt is %s", salt));
141
142  crypt_mech = crypt_mechs_root->next;
143
144  /* by examining the first n characters of a password string we
145   * can discover what kind of password it is.  hopefully. */
146  for (;crypt_mech;)
147  {
148   if (strlen(salt) < crypt_mech->mech->crypt_token_size)
149   {
150    /* try the next mechanism instead */
151    Debug((DEBUG_DEBUG, "ircd_crypt: salt is too short, will try next mech at 0x%X", crypt_mech->next));
152    crypt_mech = crypt_mech->next;
153    continue;
154   }
155
156   Debug((DEBUG_DEBUG, "ircd_crypt: comparing %s with %s", 
157    salt, crypt_mech->mech->crypt_token));
158
159   if(0 == ircd_strncmp(crypt_mech->mech->crypt_token, salt, crypt_mech->mech->crypt_token_size))
160   {
161    Debug((DEBUG_DEBUG, "ircd_crypt: type is %s", 
162     crypt_mech->mech->shortname));
163
164    /* before we send this all off to the crypt_function, we need to remove
165       the tag from it */
166
167    /* make sure we won't end up with a password comprised entirely of 
168       a single \0 */
169    if(strlen(salt) < crypt_mech->mech->crypt_token_size + 1)
170     return NULL;
171
172    mysalt = salt + crypt_mech->mech->crypt_token_size;
173
174    if(NULL == (temp_hashed_pass = crypt_mech->mech->crypt_function(key, mysalt)))
175     return NULL;
176
177    Debug((DEBUG_DEBUG, "ircd_crypt: untagged pass is %s", temp_hashed_pass));
178
179    /* ok, now we need to prefix the password we just got back
180       with the right tag */
181    if(NULL == (hashed_pass = (char *)MyMalloc(sizeof(char)*strlen(temp_hashed_pass) + crypt_mech->mech->crypt_token_size + 1)))
182    {
183     Debug((DEBUG_MALLOC, "ircd_crypt: unable to allocate memory for temp_hashed_pass"));
184     return NULL;
185    }
186    memset(hashed_pass, 0, sizeof(char)*strlen(temp_hashed_pass)
187     +crypt_mech->mech->crypt_token_size + 1);
188    ircd_strncpy(hashed_pass, crypt_mech->mech->crypt_token, 
189     crypt_mech->mech->crypt_token_size);
190    ircd_strncpy(hashed_pass + crypt_mech->mech->crypt_token_size, temp_hashed_pass, strlen(temp_hashed_pass));
191    Debug((DEBUG_DEBUG, "ircd_crypt: tagged pass is %s", hashed_pass));
192   } else {
193    Debug((DEBUG_DEBUG, "ircd_crypt: will try next mechansim at 0x%X", 
194     crypt_mech->next));
195    crypt_mech = crypt_mech->next;
196    continue;
197   }
198   return hashed_pass;
199  }
200
201  /* try to use native crypt for an old-style (untagged) password */
202  if (strlen(salt) > 2)
203  {
204    temp_hashed_pass = (char*)ircd_crypt_native(key, salt);
205    if (!ircd_strcmp(temp_hashed_pass, salt))
206     return strdup(temp_hashed_pass);
207  }
208
209  return NULL;
210 }
211
212 /** Some basic init.
213  * This function loads initalises the crypt mechanisms linked list and 
214  * currently loads the default mechanisms (Salted MD5, Crypt() and PLAIN).  
215  * The last step is only needed while ircu is not properly modular.
216  *  
217  * When ircu is modular this will be the entry function for the ircd_crypt
218  * module.
219  * 
220 */
221 void ircd_crypt_init(void)
222 {
223
224  if((crypt_mechs_root = MyMalloc(sizeof(crypt_mechs_t))) == NULL)
225  {
226   /* awooga - can't allocate memory for the root structure */
227   Debug((DEBUG_MALLOC, "init_crypt: Could not allocate memory for crypt_mechs_root"));
228   return;
229  }
230
231  crypt_mechs_root->mech = NULL;
232  crypt_mechs_root->next = crypt_mechs_root->prev = NULL;
233
234 /* temporary kludge until we're modular.  manualy call the
235    register funtions for crypt mechanisms */
236  ircd_register_crypt_smd5();
237  ircd_register_crypt_plain();
238  ircd_register_crypt_native();
239
240 return;
241 }