1/*
2 * $Id: uams_dhx_passwd.c,v 1.29 2010-03-30 12:44:35 franklahm Exp $
3 *
4 * Copyright (c) 1990,1993 Regents of The University of Michigan.
5 * Copyright (c) 1999 Adrian Sun (asun@u.washington.edu)
6 * All Rights Reserved.  See COPYRIGHT.
7 */
8
9#ifdef HAVE_CONFIG_H
10#include "config.h"
11#endif /* HAVE_CONFIG_H */
12
13#include <atalk/standards.h>
14
15#include <stdio.h>
16#include <stdlib.h>
17#include <string.h>
18#ifdef HAVE_UNISTD_H
19#include <unistd.h>
20#endif /* HAVE_UNISTD_H */
21#ifdef HAVE_CRYPT_H
22#include <crypt.h>
23#endif /* ! HAVE_CRYPT_H */
24#ifdef HAVE_SYS_TIME_H
25#include <sys/time.h>
26#endif
27#ifdef HAVE_TIME_H
28#include <time.h>
29#endif
30#include <pwd.h>
31#ifdef SHADOWPW
32#include <shadow.h>
33#endif /* SHADOWPW */
34#if defined(GNUTLS_DHX)
35#include <gnutls/openssl.h>
36#elif defined(OPENSSL_DHX)
37#include <openssl/bn.h>
38#include <openssl/dh.h>
39#include <openssl/cast.h>
40#else /* OPENSSL_DHX */
41#include <bn.h>
42#include <dh.h>
43#include <cast.h>
44#endif /* OPENSSL_DHX */
45
46#include <atalk/logger.h>
47#include <atalk/afp.h>
48#include <atalk/uam.h>
49
50#define KEYSIZE 16
51#define PASSWDLEN 64
52#define CRYPTBUFLEN  (KEYSIZE*2)
53#define CRYPT2BUFLEN (KEYSIZE + PASSWDLEN)
54
55/* hash a number to a 16-bit quantity */
56#define dhxhash(a) ((((unsigned long) (a) >> 8) ^ \
57		     (unsigned long) (a)) & 0xffff)
58
59/* the secret key */
60static CAST_KEY castkey;
61static struct passwd *dhxpwd;
62static u_int8_t randbuf[16];
63
64#ifdef TRU64
65#include <sia.h>
66#include <siad.h>
67
68static const char *clientname;
69#endif /* TRU64 */
70
71/* dhx passwd */
72static int pwd_login(void *obj, char *username, int ulen, struct passwd **uam_pwd _U_,
73			char *ibuf, size_t ibuflen _U_,
74			char *rbuf, size_t *rbuflen)
75{
76    unsigned char iv[] = "CJalbert";
77    u_int8_t p[] = {0xBA, 0x28, 0x73, 0xDF, 0xB0, 0x60, 0x57, 0xD4,
78		    0x3F, 0x20, 0x24, 0x74, 0x4C, 0xEE, 0xE7, 0x5B };
79    u_int8_t g = 0x07;
80#ifdef SHADOWPW
81    struct spwd *sp;
82#endif /* SHADOWPW */
83    BIGNUM *bn, *gbn, *pbn;
84    u_int16_t sessid;
85    size_t i;
86    DH *dh;
87
88#ifdef TRU64
89    int rnd_seed[256];
90    for (i = 0; i < 256; i++)
91        rnd_seed[i] = random();
92    RAND_seed(rnd_seed, sizeof(rnd_seed));
93#endif /* TRU64 */
94
95    *rbuflen = 0;
96
97#ifdef TRU64
98    if( uam_afpserver_option( obj, UAM_OPTION_CLIENTNAME,
99                              (void *) &clientname, NULL ) < 0 )
100        return AFPERR_PARAM;
101#endif /* TRU64 */
102
103    if (( dhxpwd = uam_getname(obj, username, ulen)) == NULL ) {
104        return AFPERR_NOTAUTH;
105    }
106
107    LOG(log_info, logtype_uams, "dhx login: %s", username);
108    if (uam_checkuser(dhxpwd) < 0)
109      return AFPERR_NOTAUTH;
110
111#ifdef SHADOWPW
112    if (( sp = getspnam( dhxpwd->pw_name )) == NULL ) {
113	LOG(log_info, logtype_uams, "no shadow passwd entry for %s", username);
114	return AFPERR_NOTAUTH;
115    }
116    dhxpwd->pw_passwd = sp->sp_pwdp;
117#endif /* SHADOWPW */
118
119    if (!dhxpwd->pw_passwd)
120      return AFPERR_NOTAUTH;
121
122    /* get the client's public key */
123    if (!(bn = BN_bin2bn((unsigned char *)ibuf, KEYSIZE, NULL))) {
124      return AFPERR_PARAM;
125    }
126
127    /* get our primes */
128    if (!(gbn = BN_bin2bn(&g, sizeof(g), NULL))) {
129      BN_free(bn);
130      return AFPERR_PARAM;
131    }
132
133    if (!(pbn = BN_bin2bn(p, sizeof(p), NULL))) {
134      BN_free(gbn);
135      BN_free(bn);
136      return AFPERR_PARAM;
137    }
138
139    /* okay, we're ready */
140    if (!(dh = DH_new())) {
141      BN_free(pbn);
142      BN_free(gbn);
143      BN_free(bn);
144      return AFPERR_PARAM;
145    }
146
147    /* generate key and make sure we have enough space */
148    dh->p = pbn;
149    dh->g = gbn;
150    if (!DH_generate_key(dh) || (BN_num_bytes(dh->pub_key) > KEYSIZE)) {
151      goto passwd_fail;
152    }
153
154    /* figure out the key. use rbuf as a temporary buffer. */
155    i = DH_compute_key((unsigned char *)rbuf, bn, dh);
156
157    /* set the key */
158    CAST_set_key(&castkey, i, (unsigned char *)rbuf);
159
160    /* session id. it's just a hashed version of the object pointer. */
161    sessid = dhxhash(obj);
162    memcpy(rbuf, &sessid, sizeof(sessid));
163    rbuf += sizeof(sessid);
164    *rbuflen += sizeof(sessid);
165
166    /* send our public key */
167    BN_bn2bin(dh->pub_key, (unsigned char *)rbuf);
168    rbuf += KEYSIZE;
169    *rbuflen += KEYSIZE;
170
171    /* buffer to be encrypted */
172    i = sizeof(randbuf);
173    if (uam_afpserver_option(obj, UAM_OPTION_RANDNUM, (void *) randbuf,
174			     &i) < 0) {
175      *rbuflen = 0;
176      goto passwd_fail;
177    }
178    memcpy(rbuf, &randbuf, sizeof(randbuf));
179
180#if 0
181    /* get the signature. it's always 16 bytes. */
182    if (uam_afpserver_option(obj, UAM_OPTION_SIGNATURE,
183			     (void *) &name, NULL) < 0) {
184      *rbuflen = 0;
185      goto passwd_fail;
186    }
187    memcpy(rbuf + KEYSIZE, name, KEYSIZE);
188#else /* 0 */
189    memset(rbuf + KEYSIZE, 0, KEYSIZE);
190#endif /* 0 */
191
192    /* encrypt using cast */
193    CAST_cbc_encrypt((unsigned char *)rbuf, (unsigned char *)rbuf, CRYPTBUFLEN, &castkey, iv, CAST_ENCRYPT);
194    *rbuflen += CRYPTBUFLEN;
195    BN_free(bn);
196    DH_free(dh);
197    return AFPERR_AUTHCONT;
198
199passwd_fail:
200    BN_free(bn);
201    DH_free(dh);
202    return AFPERR_PARAM;
203}
204
205/* cleartxt login */
206static int passwd_login(void *obj, struct passwd **uam_pwd,
207			char *ibuf, size_t ibuflen,
208			char *rbuf, size_t *rbuflen)
209{
210    char *username;
211    size_t len, ulen;
212
213    *rbuflen = 0;
214
215    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
216			     (void *) &username, &ulen) < 0)
217	return AFPERR_MISC;
218
219    if (ibuflen < 2) {
220	return( AFPERR_PARAM );
221    }
222
223    len = (unsigned char) *ibuf++;
224    ibuflen--;
225    if (!len || len > ibuflen || len > ulen ) {
226	return( AFPERR_PARAM );
227    }
228    memcpy(username, ibuf, len );
229    ibuf += len;
230    ibuflen -=len;
231    username[ len ] = '\0';
232
233    if ((unsigned long) ibuf & 1) { /* pad character */
234	++ibuf;
235	ibuflen--;
236    }
237    return (pwd_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
238
239}
240
241/* cleartxt login ext
242 * uname format :
243    byte      3
244    2 bytes   len (network order)
245    len bytes utf8 name
246*/
247static int passwd_login_ext(void *obj, char *uname, struct passwd **uam_pwd,
248			char *ibuf, size_t ibuflen,
249			char *rbuf, size_t *rbuflen)
250{
251    char       *username;
252    size_t     len, ulen;
253    u_int16_t  temp16;
254
255    *rbuflen = 0;
256
257    if (uam_afpserver_option(obj, UAM_OPTION_USERNAME,
258			     (void *) &username, &ulen) < 0)
259	return AFPERR_MISC;
260
261    if (*uname != 3)
262	return AFPERR_PARAM;
263    uname++;
264    memcpy(&temp16, uname, sizeof(temp16));
265    len = ntohs(temp16);
266    if (!len || len > ulen ) {
267	return( AFPERR_PARAM );
268    }
269    memcpy(username, uname +2, len );
270    username[ len ] = '\0';
271    return (pwd_login(obj, username, ulen, uam_pwd, ibuf, ibuflen, rbuf, rbuflen));
272}
273
274static int passwd_logincont(void *obj, struct passwd **uam_pwd,
275			    char *ibuf, size_t ibuflen _U_,
276			    char *rbuf, size_t *rbuflen)
277{
278#ifdef SHADOWPW
279    struct spwd *sp;
280#endif /* SHADOWPW */
281    unsigned char iv[] = "LWallace";
282    BIGNUM *bn1, *bn2, *bn3;
283    u_int16_t sessid;
284    char *p;
285    int err = AFPERR_NOTAUTH;
286
287    *rbuflen = 0;
288
289    /* check for session id */
290    memcpy(&sessid, ibuf, sizeof(sessid));
291    if (sessid != dhxhash(obj))
292      return AFPERR_PARAM;
293    ibuf += sizeof(sessid);
294
295    /* use rbuf as scratch space */
296    CAST_cbc_encrypt((unsigned char *)ibuf, (unsigned char *)rbuf, CRYPT2BUFLEN, &castkey,
297		     iv, CAST_DECRYPT);
298
299    /* check to make sure that the random number is the same. we
300     * get sent back an incremented random number. */
301    if (!(bn1 = BN_bin2bn((unsigned char *)rbuf, KEYSIZE, NULL)))
302      return AFPERR_PARAM;
303
304    if (!(bn2 = BN_bin2bn(randbuf, sizeof(randbuf), NULL))) {
305      BN_free(bn1);
306      return AFPERR_PARAM;
307    }
308
309    /* zero out the random number */
310    memset(rbuf, 0, sizeof(randbuf));
311    memset(randbuf, 0, sizeof(randbuf));
312    rbuf += KEYSIZE;
313
314    if (!(bn3 = BN_new())) {
315      BN_free(bn2);
316      BN_free(bn1);
317      return AFPERR_PARAM;
318    }
319
320    BN_sub(bn3, bn1, bn2);
321    BN_free(bn2);
322    BN_free(bn1);
323
324    /* okay. is it one more? */
325    if (!BN_is_one(bn3)) {
326      BN_free(bn3);
327      return AFPERR_PARAM;
328    }
329    BN_free(bn3);
330
331    rbuf[PASSWDLEN] = '\0';
332#ifdef TRU64
333    {
334        int ac;
335        char **av;
336        char hostname[256];
337
338        uam_afp_getcmdline( &ac, &av );
339        sprintf( hostname, "%s@%s", dhxpwd->pw_name, clientname );
340
341        if( uam_sia_validate_user( NULL, ac, av, hostname, dhxpwd->pw_name,
342                                   NULL, FALSE, NULL, rbuf ) != SIASUCCESS )
343            return AFPERR_NOTAUTH;
344
345        memset( rbuf, 0, PASSWDLEN );
346        *uam_pwd = dhxpwd;
347        return AFP_OK;
348    }
349#else /* TRU64 */
350    p = crypt( rbuf, dhxpwd->pw_passwd );
351    memset(rbuf, 0, PASSWDLEN);
352    if ( strcmp( p, dhxpwd->pw_passwd ) == 0 ) {
353      *uam_pwd = dhxpwd;
354      err = AFP_OK;
355    }
356#ifdef SHADOWPW
357    if (( sp = getspnam( dhxpwd->pw_name )) == NULL ) {
358	LOG(log_info, logtype_uams, "no shadow passwd entry for %s", dhxpwd->pw_name);
359	return (AFPERR_NOTAUTH);
360    }
361
362    /* check for expired password */
363    if (sp && sp->sp_max != -1 && sp->sp_lstchg) {
364        time_t now = time(NULL) / (60*60*24);
365        int32_t expire_days = sp->sp_lstchg - now + sp->sp_max;
366        if ( expire_days < 0 ) {
367                LOG(log_info, logtype_uams, "password for user %s expired", dhxpwd->pw_name);
368		err = AFPERR_PWDEXPR;
369        }
370    }
371#endif /* SHADOWPW */
372    return err;
373#endif /* TRU64 */
374
375    return AFPERR_NOTAUTH;
376}
377
378
379static int uam_setup(const char *path)
380{
381  if (uam_register(UAM_SERVER_LOGIN_EXT, path, "DHCAST128",
382		   passwd_login, passwd_logincont, NULL, passwd_login_ext) < 0)
383    return -1;
384  /*uam_register(UAM_SERVER_PRINTAUTH, path, "DHCAST128",
385    passwd_printer);*/
386
387  return 0;
388}
389
390static void uam_cleanup(void)
391{
392  uam_unregister(UAM_SERVER_LOGIN, "DHCAST128");
393  /*uam_unregister(UAM_SERVER_PRINTAUTH, "DHCAST128"); */
394}
395
396UAM_MODULE_EXPORT struct uam_export uams_dhx = {
397  UAM_MODULE_SERVER,
398  UAM_MODULE_VERSION,
399  uam_setup, uam_cleanup
400};
401
402UAM_MODULE_EXPORT struct uam_export uams_dhx_passwd = {
403  UAM_MODULE_SERVER,
404  UAM_MODULE_VERSION,
405  uam_setup, uam_cleanup
406};
407