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