root/pam_freeauth/pam_freeauth.c

Revision 10, 15.8 kB (checked in by evilbunny, 2 years ago)

fixed secret change code

Line 
1
2 #define USERSFILE "/etc/security/freeauth.conf"
3
4 #define STATFILE "/var/cache/freeauth"
5
6 /*
7   secret   = init string created by #**#
8   passcode = string user types in
9   otp      = calculated by server, must match passcode
10 */
11
12 #ifdef linux
13 #define LINUX
14 #endif
15
16 #define LEN_SECRET    16
17 #define LEN_PASSCODE  10
18 #define LEN_OTP        LEN_PASSCODE
19
20 #define MAX_DIFF 6
21
22
23 /* partly stolen from pam_pwdfile.c */
24
25 #include <stdio.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <syslog.h>
29 #include <sys/stat.h>
30 #include <sys/mman.h>
31 #include <fcntl.h>
32
33 #include <security/pam_appl.h>
34
35 #define PAM_SM_AUTH
36 #define PAM_SM_PASSWORD
37 #include <security/pam_modules.h>
38
39 #ifdef SOLARIS
40 #define PAM_EXTERN
41 #endif
42
43 #ifdef LINUX
44 #include "md5.h"
45 #endif
46
47 #define TO_STR(str) TO_STR_2(str)
48 #define TO_STR_2(str) #str
49
50
51 // #define TRACE fprintf
52 #define TRACE //
53
54 #define BUFSIZE 256
55
56 #define _PAM_LOG if (!(flags & PAM_SILENT)) _pam_log
57 /* logging function ripped from pam_listfile.c */
58 void _pam_log(int err, const char *format, ...) {
59   va_list args;
60   va_start(args, format);
61   openlog("pam_freeauth", LOG_CONS|LOG_PID, LOG_AUTH);
62   vsyslog(err, format, args);
63   // vfprintf(stderr, format, args ); fputc('\n',stderr);
64   va_end(args);
65   closelog();
66 }
67
68 #define NOT_IMPLEMENTED(fct) \
69      _pam_log (LOG_ERR, "error: " #fct " is not implemented by pam_freeauth")
70
71 PAM_EXTERN int pam_sm_open_session(pam_handle_t *pamh, int flags,
72                                    int argc, const char **argv) {
73   NOT_IMPLEMENTED (session management);
74   return PAM_SERVICE_ERR;
75 }
76
77 PAM_EXTERN int pam_sm_close_session(pam_handle_t *pamh, int flags,
78                                     int argc, const char **argv) {
79   NOT_IMPLEMENTED (session management);
80   return PAM_SERVICE_ERR;
81 }
82
83 PAM_EXTERN int pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
84                                 int argc, const char **argv) {
85   NOT_IMPLEMENTED (account management);
86   return PAM_SERVICE_ERR;
87 }
88
89
90
91 PAM_EXTERN int pam_sm_authenticate(pam_handle_t *pamh, int flags,
92                                    int argc, const char **argv) {   
93   int retval, i;
94   const char *usersfile = USERSFILE;
95   const char *statfile = STATFILE;
96   char secret[LEN_SECRET+1], passcode[LEN_PASSCODE+1];
97   const char *user;
98   char buffer [BUFSIZE+1];
99   long unsigned int now;
100
101   short debug = 0;
102   short no_warn = 0;
103   short use_first_pass = 0;
104   short try_first_pass = 0;
105   short not_set_pass = 0;
106
107   char * getOTP64(long unsigned int now, char *secret) {
108     unsigned char lookupChar[] = "123456789abcdefhkmnprstuvwxyzABCDEFGHKMNPQRSTUVWXYZ=+[]&@#*!-?%:";
109     unsigned char md5sum[16];
110     int i, tmp1 = 0, tmp2 = 0;
111     snprintf ( buffer, BUFSIZE, "%0ld%s", now, secret);
112 #ifdef LINUX
113     md5_buffer ( buffer, strlen(buffer), md5sum );
114 #else /* Solaris */
115     md5_calc ( md5sum, buffer, strlen(buffer) );
116 #endif
117
118     tmp1 = md5sum[0] >> 2;
119     buffer[0] = lookupChar[tmp1 & 63];
120     tmp2 = md5sum[0] - (tmp1 << 2);
121     tmp1 = md5sum[1] >> 4;
122     buffer[1] = lookupChar[(tmp1 + tmp2) & 63];
123     tmp2 = md5sum[1] - (tmp1 << 4);
124     tmp1 = md5sum[2] >> 6;
125     buffer[2] = lookupChar[(tmp1 + tmp2) & 63];
126     tmp2 = md5sum[2] - (tmp1 << 6);
127     buffer[3] = lookupChar[tmp2 & 63];
128     tmp1 = md5sum[3] >> 2;
129     buffer[4] = lookupChar[tmp1 & 63];
130     tmp2 = md5sum[3] - (tmp1 << 2);
131     tmp1 = md5sum[4] >> 4;
132     buffer[5] = lookupChar[(tmp1 + tmp2) & 63];
133     tmp2 = md5sum[4] - (tmp1 << 4);
134     tmp1 = md5sum[5] >> 6;
135     buffer[6] = lookupChar[(tmp1 + tmp2) & 63];
136     tmp2 = md5sum[5] - (tmp1 << 6);
137     buffer[7] = lookupChar[tmp2 & 63];
138
139     buffer[8] = 0;
140     TRACE(stderr,"now=%ld, secret=%s, otp=%s\n",now,secret,buffer);
141     return buffer;
142   }
143  
144   char * getOTP32(long unsigned int now, char *secret) {
145     unsigned char lookupChar[] = "0123456789abcdefghkmnoprstuvwxyz";
146     unsigned char md5sum[16];
147     int i, tmp1 = 0, tmp2 = 0;
148     snprintf(buffer, BUFSIZE, "%0ld%s", now, secret);
149 #ifdef LINUX
150     md5_buffer(buffer, strlen(buffer), md5sum);
151 #else /* Solaris */
152     md5_calc(md5sum, buffer, strlen(buffer));
153 #endif
154
155     tmp1 = md5sum[0] >> 3;
156     buffer[0] = lookupChar[tmp1 & 31];
157     tmp2 = md5sum[0] - (tmp1 << 3);
158     tmp1 = md5sum[1] >> 6;
159     buffer[1] = lookupChar[(tmp1 + tmp2) & 31];
160     tmp2 = (md5sum[1] - (tmp1 << 6)) >> 1;
161     buffer[2] = lookupChar[tmp2 & 31];
162     tmp2 = md5sum[1] - ((md5sum[1] >> 1) << 1);
163     tmp1 = md5sum[2] >> 4;
164     buffer[3] = lookupChar[(tmp1 + tmp2) & 31];
165     tmp2 = md5sum[2] - (tmp1 << 4);
166     tmp1 = md5sum[3] >> 7;
167     buffer[4] = lookupChar[(tmp1 + tmp2) & 31];
168     tmp2 = (md5sum[3] - (tmp1 << 7)) >> 2;
169     buffer[5] = lookupChar[tmp2 & 31];
170     tmp2 = md5sum[3] - ((md5sum[3] - (tmp1 << 7)) >> 2) << 2;
171     tmp1 = md5sum[4] >> 5;
172     buffer[6] = lookupChar[(tmp1 + tmp2) & 31];
173     tmp2 = md5sum[4] - (tmp1 << 5);
174     buffer[7] = lookupChar[tmp2 & 31];
175     tmp1 = md5sum[5] >> 3;
176     buffer[8] = lookupChar[tmp1 & 31];
177     tmp2 = md5sum[5] - (tmp1 << 3);
178     tmp1 = md5sum[6] >> 6;
179     buffer[9] = lookupChar[(tmp1 + tmp2) & 31];
180
181     buffer[10] = 0;
182     TRACE(stderr,"now=%ld, secret=%s, otp=%s\n",now,secret,buffer);
183     return buffer;
184   }
185  
186   /* read options */
187   for (i=0; i<argc; i++) { 
188     if (strncmp(argv[i],"users=",strlen("users="))==0) {
189       usersfile = strchr( argv[i], '=');
190       usersfile++;
191     }
192     else if (strncmp(argv[i],"cache=",strlen("cache="))==0) {
193       statfile = strchr( argv[i], '=');
194       statfile++;
195     }
196     else if (strcmp(argv[i],"debug")==0)
197       debug=1;
198     else if (strcmp(argv[i],"no_warn")==0)
199       no_warn=1;
200     else if (strcmp(argv[i],"use_first_pass")==0)
201       use_first_pass=1;
202     else if (strcmp(argv[i],"try_first_pass")==0)
203       try_first_pass=1;
204     else if (strcmp(argv[i],"not_set_pass")==0)
205       not_set_pass=1;
206     else
207       _PAM_LOG(LOG_ERR,"option not implemented: %s",argv[i]);
208   }
209
210
211   /* read user name */
212   if ((retval=pam_get_user(pamh,&user,NULL)) != PAM_SUCCESS) {
213     _PAM_LOG(LOG_ERR, "error: get username");
214     return retval;
215   }
216   TRACE(stderr,"USER: %s\n",user);
217  
218
219   /* get passcode */
220  get_passcode:
221   if (use_first_pass || try_first_pass) {
222     char *p;
223     retval = pam_get_item(pamh,PAM_AUTHTOK,(void *)&p);
224     if (retval != PAM_SUCCESS)
225 #ifdef LINUX
226       return PAM_AUTHTOK_RECOVER_ERR;
227 #else /* Solaris */
228       return PAM_AUTHINFO_UNAVAIL;
229 #endif
230     if (p) { strncpy(passcode, p ,LEN_PASSCODE); passcode[LEN_PASSCODE]=0; }
231     TRACE(stderr,"old passcode: %s\n",passcode);
232     if (debug) _PAM_LOG(LOG_DEBUG, "using old passcode (%s)",
233                         use_first_pass?"use_first_pass":"try_first_pass");
234   }
235   else {
236     struct pam_message msg, *pmsg[1];
237     struct pam_response *resp;
238     struct pam_conv *conv;
239     msg.msg = "passcode: ";
240     msg.msg_style = PAM_PROMPT_ECHO_OFF;
241     pmsg[0] = &msg;
242    
243     retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
244     if (retval != PAM_SUCCESS) return retval;
245     retval = conv->conv (1, (const struct pam_message **)pmsg, &resp, conv->appdata_ptr);
246     if (retval != PAM_SUCCESS) return retval;
247     if (!resp) return PAM_CONV_ERR;
248     strncpy(passcode, resp->resp ,LEN_PASSCODE); passcode[LEN_PASSCODE]=0;
249     if (resp) free (resp);
250     if (!not_set_pass) pam_set_item (pamh, PAM_AUTHTOK, &passcode);
251   }
252   TRACE(stderr,"user: %s, passcode: %s\n", user, passcode);
253  
254  
255   /* read secret from users file */
256   {
257     FILE *fp;
258     char name[BUFSIZE+1]; name[0]=0;
259     if ( (fp=fopen(usersfile,"r")) == NULL ) {
260       _PAM_LOG(LOG_ALERT, "error: cannot open usersfile %s",usersfile);
261       return PAM_AUTHINFO_UNAVAIL;
262     }
263     while (fgets(buffer,BUFSIZE,fp)) {
264       if ( (buffer[0]=='#') || (buffer[0]==0) ) continue;
265       buffer[BUFSIZE]=0;
266       TRACE(stderr,"buffer=%s\n",buffer);
267       retval = sscanf(buffer, "%s\t%s", &name, &secret);
268       if (retval < 2) continue;
269       TRACE(stderr,"trying: user: %s, secret: %s -- user: %s\n", name, secret, user);
270       if ( strcmp(user,name)==0 ) break;
271     }
272     if ( fclose(fp) ) if (!no_warn)
273       _PAM_LOG(LOG_INFO, "warning: cannot close usersfile %s",usersfile);
274     if ( strcmp(user,name) ) {
275       _PAM_LOG(LOG_INFO, "user %s not found in file %s", user,usersfile);
276       return PAM_USER_UNKNOWN;
277     }
278     TRACE(stderr,"user: %s, secret: %s\n", name, secret);
279     if (debug) _PAM_LOG(LOG_DEBUG, "user: %s", name);
280   }
281  
282  
283   /* passcode ok? */
284   retval=PAM_AUTH_ERR;
285   now = time(NULL);     
286   now /= 60;
287   now -= MAX_DIFF/2;
288   i = now + MAX_DIFF;
289   if (debug) _PAM_LOG(LOG_NOTICE, passcode);
290   if (debug) _PAM_LOG(LOG_NOTICE, secret);
291   for ( ; now <= i ; now++ )
292   {
293     if(strlen(passcode) == 8)
294     {
295       if (debug) _PAM_LOG(LOG_NOTICE, getOTP64(now,secret));
296       if (strcmp(passcode,getOTP64(now,secret))==0) {
297         if (debug) _PAM_LOG(LOG_DEBUG, "passcode accepted");
298         retval=PAM_SUCCESS;
299         break;
300       }
301     } else {
302       if (debug) _PAM_LOG(LOG_NOTICE, getOTP32(now,secret));
303       if (strcmp(passcode,getOTP32(now,secret))==0) {
304         if (debug) _PAM_LOG(LOG_DEBUG, "passcode accepted");
305         retval=PAM_SUCCESS;
306         break;
307       }
308     }
309   }
310   if ( (retval!=PAM_SUCCESS) && (try_first_pass) ) {
311     TRACE(stderr,"trying without \"try_first_pass\"\n");
312     try_first_pass = 0;
313     goto get_passcode;
314   }
315   if (retval!=PAM_SUCCESS) _PAM_LOG(LOG_NOTICE, "passcode not accepted");
316   TRACE (stderr, "passcode %saccepted: %s\n", (retval==PAM_SUCCESS?"":"not "), passcode);
317  
318
319   /* read and write user's status file */
320   {
321     FILE *fp;
322     int tries = 0;
323     long unsigned int last_time = 0;
324    
325     snprintf(buffer,BUFSIZE,"%s/%s",statfile,user);
326     buffer[BUFSIZE]=0;
327     statfile=buffer;
328     if ( (fp=fopen(statfile,"r")) == NULL ) {
329       if (debug) _PAM_LOG(LOG_DEBUG, "user %s has no statfile",user);
330       tries = last_time = 0;
331     }
332     else {
333       if ( fscanf(fp,"%d:%lu:%*s",&tries,&last_time) != 2 ) {
334         _PAM_LOG(LOG_INFO, "cannot read data from statfile",statfile);
335         fclose(fp);
336         return PAM_AUTHINFO_UNAVAIL;
337       }
338       tries = 0;
339       if (debug) _PAM_LOG(LOG_DEBUG, "user has tries: %d, last time: %lu", tries, last_time);
340       if ( fclose(fp) ) if (!no_warn)
341         _PAM_LOG(LOG_INFO, "warning: cannot close file (r) %s",statfile);
342     }
343    
344     if (!(now>last_time))  // check of replays
345       retval=PAM_AUTH_ERR;
346     if (retval == PAM_SUCCESS)  // reset tries
347       { tries = 0; last_time = now; }
348     else
349       tries ++;
350    
351     umask(077);
352     if ( (fp=fopen(statfile,"w")) == NULL ) {
353       _PAM_LOG(LOG_ERR, "cannot write statfile %s",statfile);
354       return PAM_AUTHINFO_UNAVAIL;
355     }
356     if ( fprintf(fp,"%d:%lu:%s\n",tries,last_time,"") < 0 ) {
357       _PAM_LOG(LOG_INFO, "cannot write new statfile %s",statfile);
358       retval = PAM_AUTHINFO_UNAVAIL;
359     }
360     TRACE(stderr,"written new stats - tries: %d, last time: %lu\n",tries,last_time);
361     if ( fclose(fp) ) if (!no_warn)
362       _PAM_LOG(LOG_INFO, "warning: cannot close file (w) %s",statfile);
363   }
364  
365
366   /* return pam code */
367   return retval;
368 }
369
370
371 PAM_EXTERN int pam_sm_setcred(pam_handle_t *pamh, int flags,
372                               int argc, const char **argv)
373 {
374   return PAM_SUCCESS;
375 }
376
377
378 PAM_EXTERN int pam_sm_chauthtok(pam_handle_t *pamh, int flags,
379                                 int argc, const char **argv) {
380  
381   /*
382     Function is not thread save at the moment.
383     Moreover, changing usersfile while user changes secret is problematic.
384     User must not type in old secret :-(
385   */
386  
387   int retval, i;
388   const char *usersfile = USERSFILE;
389   char SECRET[LEN_SECRET+1];
390   const char *user;
391   char buffer[BUFSIZE+1], name[BUFSIZE+1];
392   int pos = 0;
393   FILE *fp;
394  
395   short debug = 0;
396   short no_warn = 0;
397   short use_first_pass = 0;
398   short try_first_pass = 0;
399   short not_set_pass = 0;
400
401   /* read options */
402   for (i=0; i<argc; i++) {
403     if (strncmp(argv[i],"users=",strlen("users="))==0) {
404       usersfile = strchr( argv[i], '=');
405       usersfile++;
406     }
407     else if (strcmp(argv[i],"debug")==0)
408       debug=1;
409     else if (strcmp(argv[i],"no_warn")==0)
410       no_warn=1;
411     else
412       _PAM_LOG(LOG_ERR,"option not implemented: %s",argv[i]);
413   }
414  
415  
416   /* read user name */
417   if ((retval=pam_get_user(pamh,&user,NULL)) != PAM_SUCCESS) {
418     _PAM_LOG(LOG_ERR, "error: get username");
419     return retval;
420   }
421
422   if (debug) _PAM_LOG(LOG_DEBUG, "user %s tries to change SECRET - %s",
423                       user, (flags & PAM_PRELIM_CHECK) ? "1st run" : "2nd run" );
424
425   if (flags & PAM_PRELIM_CHECK) {
426     /* read secret from users file */
427     {
428       char *p;
429       name[0]=0;
430       if ( (fp=fopen(usersfile,"r")) == NULL ) {
431         _PAM_LOG(LOG_ALERT, "error: cannot open usersfile %s",usersfile);
432         return PAM_TRY_AGAIN;
433       }
434       while (fgets(buffer,BUFSIZE,fp)) {
435         pos += strlen(buffer);
436         if ( (buffer[0]=='#') || (buffer[0]==0) ) continue;
437         buffer[BUFSIZE]=0;
438         if ( !sscanf(buffer, "%s\t%s", &name, &SECRET)) continue;
439         if ( strcmp(user,name)==0 ) break;
440       }
441       if ( fclose(fp) ) if (!no_warn)
442         _PAM_LOG(LOG_INFO, "warning: cannot close usersfile %s",usersfile);
443       if ( strcmp(user,name) ) {
444         _PAM_LOG(LOG_INFO, "user %s not found in file %s", user,usersfile);
445         pos=0;
446         return PAM_USER_UNKNOWN;
447       }
448       pos -= strlen(buffer);
449       p=buffer;
450       while ( p && (! isspace(*p)) ) p++;  // skip name
451       while ( p &&    isspace(*p)  ) p++;  // skip gap
452       pos += (p-buffer);
453       retval = pam_set_data (pamh, "freeauth-position", (void *)pos, NULL);
454       if (retval != PAM_SUCCESS) return PAM_TRY_AGAIN;
455       TRACE(stderr,"position: %d\n",pos);
456     }
457     return PAM_SUCCESS;
458   }
459
460
461   /* get new SECRET */
462   {
463     struct pam_message msg, *pmsg[1];
464     struct pam_response *resp;
465     struct pam_conv *conv;
466     int tries = 0;
467
468     void message ( pam_handle_t *pamh, char *str ) {
469       msg.msg_style = PAM_ERROR_MSG;
470       msg.msg = str;
471       retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
472       if (retval != PAM_SUCCESS) return;
473       retval = conv->conv (1, (const struct pam_message **)pmsg,
474                            &resp, conv->appdata_ptr);
475       if (resp) free (resp);
476     }
477
478     pmsg[0] = &msg;
479
480   new_secret:
481     if (++tries > 3) return PAM_AUTHTOK_ERR;
482
483     msg.msg_style = PAM_PROMPT_ECHO_OFF;
484     msg.msg = "new SECRET: ";
485     retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
486     if (retval != PAM_SUCCESS) return retval;
487     retval = conv->conv (1, (const struct pam_message **)pmsg, &resp, conv->appdata_ptr);
488     if (retval != PAM_SUCCESS) return retval;
489     if (!resp) return PAM_CONV_ERR;
490     strncpy(SECRET, resp->resp ,LEN_SECRET); SECRET[LEN_SECRET]=0;
491
492     if ( strlen(resp->resp) > 16 )
493       message(pamh, "SECRET will be truncated to 16 characters");
494     free (resp);
495
496     if (strlen(SECRET)<16) {
497       message(pamh, "SECRET must be 16 characters long -- please retry\n");
498       goto new_secret;
499     }
500
501     msg.msg_style = PAM_PROMPT_ECHO_OFF;
502     msg.msg = "new SECRET again: ";
503     pmsg[0] = &msg;
504     retval = pam_get_item (pamh, PAM_CONV, (const void **) &conv);
505     if (retval != PAM_SUCCESS) return retval;
506     retval = conv->conv (1, (const struct pam_message **)pmsg, &resp, conv->appdata_ptr);
507     if (retval != PAM_SUCCESS) return retval;
508     if (!resp) return PAM_CONV_ERR;
509
510     if ( strlen(resp->resp) > 16 )
511       message(pamh, "SECRET will be truncated to 16 characters");
512    
513     if ( strncmp(SECRET, resp->resp, strlen(SECRET)) ) {
514       free (resp);
515       message(pamh, "SECRETs are different -- please retry");
516       goto new_secret;
517     }
518     free (resp);
519   }
520  
521
522   /* open usersfile for writing and change SECRET */
523   retval = pam_get_data (pamh, "freeauth-position", (const void **) &pos);
524   if (retval != PAM_SUCCESS) return retval;
525   if ( (fp=fopen(usersfile,"r+")) == NULL ) {
526     _PAM_LOG(LOG_ALERT, "error: cannot open usersfile %s",usersfile);
527     return PAM_PERM_DENIED;
528   }
529   TRACE(stderr,"position: %d\n",pos);
530   for (;pos--;) if (fgetc(fp)==EOF) return PAM_PERM_DENIED;
531   fflush(fp);
532   if ( fputs(SECRET,fp) == EOF ) return PAM_PERM_DENIED;
533   if ( fclose(fp) ) if (!no_warn)
534     _PAM_LOG(LOG_INFO, "warning: cannot close usersfile %s",usersfile);
535  
536   if (debug) _PAM_LOG(LOG_DEBUG, "SECRET changed" );
537   return PAM_SUCCESS;
538 }
539
540
541 #ifdef PAM_STATIC
542 struct pam_module _pam_freeauth_modstruct = {
543   "pam_freeauth",
544   pam_sm_authenticate,
545   pam_sm_setcred,
546   NULL,
547   NULL,
548   NULL,
549   pam_sm_chauthtok,
550 };
551 #endif
Note: See TracBrowser for help on using the browser.