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