]> jfr.im git - irc/quakenet/snircd.git/blob - ircd/ircd_crypt.c
Initial import of 2.10.12.01
[irc/quakenet/snircd.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: ircd_crypt.c,v 1.7 2005/06/24 13:57:21 a1kmm Exp $
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 beginning 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 Blowfish, 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_log.h"
51 #include "ircd_string.h"
52 #include "s_debug.h"
53
54 /* while we're not modular, we need their init functions */
55 #include "ircd_crypt_native.h"
56 #include "ircd_crypt_plain.h"
57 #include "ircd_crypt_smd5.h"
58
59 /* #include <assert.h> -- Now using assert in ircd_log.h */
60 #include <unistd.h>
61 #include <string.h>
62
63 /* evil global */
64 crypt_mechs_t* crypt_mechs_root;
65
66 /** Add a crypt mechanism to the list
67 * @param mechanism Pointer to the mechanism details struct
68 * @return 0 on success, anything else on fail.
69 *
70 * This routine registers a new crypt mechanism in the loaded mechanisms list,
71 * making it availabe for comparing passwords.
72 */
73 int ircd_crypt_register_mech(crypt_mech_t* mechanism)
74 {
75 crypt_mechs_t* crypt_mech;
76
77 Debug((DEBUG_INFO, "ircd_crypt_register_mech: registering mechanism: %s", mechanism->shortname));
78
79 /* try to allocate some memory for the new mechanism */
80 if ((crypt_mech = (crypt_mechs_t*)MyMalloc(sizeof(crypt_mechs_t))) == NULL)
81 {
82 /* aww poot, we couldn't get any memory, scream a little then back out */
83 Debug((DEBUG_MALLOC, "ircd_crypt_register_mech: could not allocate memory for %s", mechanism->shortname));
84 return -1;
85 }
86
87 /* ok, we have memory, initialise it */
88 memset(crypt_mech, 0, sizeof(crypt_mechs_t));
89
90 /* assign the data */
91 crypt_mech->mech = mechanism;
92 crypt_mech->next = crypt_mech->prev = NULL;
93
94 /* first of all, is there anything there already? */
95 if(crypt_mechs_root->next == NULL)
96 {
97 /* nope, just add ourself */
98 crypt_mechs_root->next = crypt_mechs_root->prev = crypt_mech;
99 } else {
100 /* nice and simple, put ourself at the end */
101 crypt_mech->prev = crypt_mechs_root->prev;
102 crypt_mech->next = NULL;
103 crypt_mechs_root->prev = crypt_mech->prev->next = crypt_mech;
104 }
105
106 /* we're done */
107 Debug((DEBUG_INFO, "ircd_crypt_register_mech: registered mechanism: %s, crypt_function is at 0x%X.", crypt_mech->mech->shortname, &crypt_mech->mech->crypt_function));
108 Debug((DEBUG_INFO, "ircd_crypt_register_mech: %s: %s", crypt_mech->mech->shortname, crypt_mech->mech->description));
109 return 0;
110 }
111
112 /** Remove a crypt mechanism from the list
113 * @param mechanism Pointer to the mechanism we want to remove
114 * @return 0 on success, anything else on fail.
115 */
116 int ircd_crypt_unregister_mech(crypt_mech_t* mechanism)
117 {
118
119 return 0;
120 }
121
122 /** Wrapper for generating a hashed password passed on the supplied password
123 * @param key Pointer to the password we want crypted
124 * @param salt Pointer to the password we're comparing to (for the salt)
125 * @return Pointer to the generated password (must be MyFree()'d).
126 *
127 * This is a wrapper function which attempts to establish the password
128 * format and funnel it off to the correct mechanism handler function. The
129 * returned password is compared in the oper_password_match() routine.
130 */
131 char* ircd_crypt(const char* key, const char* salt)
132 {
133 char *hashed_pass = NULL;
134 const char *temp_hashed_pass, *mysalt;
135 crypt_mechs_t* crypt_mech;
136
137 assert(NULL != key);
138 assert(NULL != salt);
139
140 Debug((DEBUG_DEBUG, "ircd_crypt: key is %s", key));
141 Debug((DEBUG_DEBUG, "ircd_crypt: salt is %s", salt));
142
143 crypt_mech = crypt_mechs_root->next;
144
145 /* by examining the first n characters of a password string we
146 * can discover what kind of password it is. hopefully. */
147 for (;crypt_mech;)
148 {
149 if (strlen(salt) < crypt_mech->mech->crypt_token_size)
150 {
151 /* try the next mechanism instead */
152 Debug((DEBUG_DEBUG, "ircd_crypt: salt is too short, will try next mech at 0x%X", crypt_mech->next));
153 crypt_mech = crypt_mech->next;
154 continue;
155 }
156
157 Debug((DEBUG_DEBUG, "ircd_crypt: comparing %s with %s",
158 salt, crypt_mech->mech->crypt_token));
159
160 if(0 == ircd_strncmp(crypt_mech->mech->crypt_token, salt, crypt_mech->mech->crypt_token_size))
161 {
162 Debug((DEBUG_DEBUG, "ircd_crypt: type is %s",
163 crypt_mech->mech->shortname));
164
165 /* before we send this all off to the crypt_function, we need to remove
166 the tag from it */
167
168 /* make sure we won't end up with a password comprised entirely of
169 a single \0 */
170 if(strlen(salt) < crypt_mech->mech->crypt_token_size + 1)
171 return NULL;
172
173 mysalt = salt + crypt_mech->mech->crypt_token_size;
174
175 if(NULL == (temp_hashed_pass = crypt_mech->mech->crypt_function(key, mysalt)))
176 return NULL;
177
178 Debug((DEBUG_DEBUG, "ircd_crypt: untagged pass is %s", temp_hashed_pass));
179
180 /* ok, now we need to prefix the password we just got back
181 with the right tag */
182 if(NULL == (hashed_pass = (char *)MyMalloc(sizeof(char)*strlen(temp_hashed_pass) + crypt_mech->mech->crypt_token_size + 1)))
183 {
184 Debug((DEBUG_MALLOC, "ircd_crypt: unable to allocate memory for temp_hashed_pass"));
185 return NULL;
186 }
187 memset(hashed_pass, 0, sizeof(char)*strlen(temp_hashed_pass)
188 +crypt_mech->mech->crypt_token_size + 1);
189 ircd_strncpy(hashed_pass, crypt_mech->mech->crypt_token,
190 crypt_mech->mech->crypt_token_size);
191 ircd_strncpy(hashed_pass + crypt_mech->mech->crypt_token_size, temp_hashed_pass, strlen(temp_hashed_pass));
192 Debug((DEBUG_DEBUG, "ircd_crypt: tagged pass is %s", hashed_pass));
193 } else {
194 Debug((DEBUG_DEBUG, "ircd_crypt: will try next mechanism at 0x%X",
195 crypt_mech->next));
196 crypt_mech = crypt_mech->next;
197 continue;
198 }
199 return hashed_pass;
200 }
201
202 /* try to use native crypt for an old-style (untagged) password */
203 if (strlen(salt) > 2)
204 {
205 char *s;
206 temp_hashed_pass = (char*)ircd_crypt_native(key, salt);
207 if (!ircd_strcmp(temp_hashed_pass, salt))
208 {
209 DupString(s, temp_hashed_pass);
210 return s;
211 }
212 }
213
214 return NULL;
215 }
216
217 /** Some basic init.
218 * This function loads initalises the crypt mechanisms linked list and
219 * currently loads the default mechanisms (Salted MD5, Crypt() and PLAIN).
220 * The last step is only needed while ircu is not properly modular.
221 *
222 * When ircu is modular this will be the entry function for the ircd_crypt
223 * module.
224 *
225 */
226 void ircd_crypt_init(void)
227 {
228
229 if((crypt_mechs_root = MyMalloc(sizeof(crypt_mechs_t))) == NULL)
230 {
231 /* awooga - can't allocate memory for the root structure */
232 Debug((DEBUG_MALLOC, "init_crypt: Could not allocate memory for crypt_mechs_root"));
233 return;
234 }
235
236 crypt_mechs_root->mech = NULL;
237 crypt_mechs_root->next = crypt_mechs_root->prev = NULL;
238
239 /* temporary kludge until we're modular. manually call the
240 register functions for crypt mechanisms */
241 ircd_register_crypt_smd5();
242 ircd_register_crypt_plain();
243 ircd_register_crypt_native();
244
245 return;
246 }