/* Mailer de sendmail : traite un message de demande de changement de mot de passe : identifie l'expediteur, lit les trois mots de la ligne qui suit la premiere ligne vide de stdin et change le mot de passe du user identifie par le premier mot, envoie un message a l'expediteur. J.P. Kuypers - SRI aout 1991 Ajouts pour "shadow" mars 1995 Ajouts pour Solaris 2.x mars 1995 *|* Remerciements fougueux à Pascal Maes (ELEC) et Bruno Yernaux (SIA) \o/ pour leur aide amicale et, surtout, efficace. La source doit simplement etre compilee par une commande du genre : cc motdepasse.c -o motdepasse -s L'executable doit etre mis, par exemple dans /usr/local/lib : -rwsr-x--x 1 root staff ... /usr/local/lib/motdepasse* Dans sendmail.cf, il y a lieu d'ajouter les lignes suivantes : # Definition du mailer pour changer le mot de passe Mmdp, P=/usr/local/lib/motdepasse, F=Fl, A=motdepasse $u # Dans SO : # le courrier special pour changer le mot de passe Rmotdepasse<@$=m> $#mdp $:special */ #define HAS_SHADOW #define SOLARIS2 #include #include #include #include #include #include #ifdef HAS_SHADOW #include #endif #ifdef SOLARIS2 #define index(s,c) strchr((char *)(s),(c)) #endif #define MAXDELAI 260 #define MAXLINELEN 255 char errfile[] = "/var/adm/mdp.err"; #ifdef HAS_SHADOW #else #define PASSTEMP "/etc/ptmp" #define PASSWD "/etc/passwd" #endif char message[MAXLINELEN]; FILE *ppipe; extern int errno; /* message strings */ char *MNOFROM = "Le From: manque."; char *MMAILUNAV = "usr/ucb/mail indisponible !!"; char *MMAILEMPTY = "Le message est vide."; char *MONEWORD = "Il n'y a qu'un mot :"; char *MTWOWORD = "Il n'y a que deux mots :"; char *MLOGIN = "Le nom d'utilisateur,"; char *MUNKNOW = ", est inconnu ou l'ancien mot de passe,"; char *MINCORR = ", est incorrect."; char *MNEWPASS = "Le nouveau mot de passe,"; char *MBADBCAUSE = "est inacceptable parce qu'il est"; char *MPASSNMBER = "compose uniquement de chiffres."; char *MPASSIMPLE = "trop simple."; char *MSYSTEMERR = "Erreur dans la configuration du systeme : impossible de repondre a votre demande."; char *MBUSYWAIT = "occupe. Attente de"; char *MPWDBUSY = "Le fichier des mots de passe est occupe. Veuillez reessayer."; char *MALREADYTHERE = "deja present."; char *MOPENPWDPB = "Il y a un probleme a l'ouverture du fichier des mots de passe."; char *MOPENFAILED = "Echec a l'ouverture de"; char *MPASSNOTUPDTD = "Attention: une erreur est survenue a l'ecriture du fichier de mots de passe. Le mot de passe n'est pas modifie."; char *MPASSPHPSUPDTD = "Une erreur grave est survenue en fin de manoeuvre. Le mot de passe n'est peut-etre pas modifie"; char *MPASSWDOF = "Mot de passe de"; char *MMODIFDBY = ", modifie par"; char *MTHEPASSWDOF = "Le mot de passe de"; char *MHASBNMODIFD = "a ete modifie"; char *MBYREQUESTOF = "a la demande de"; char *sujet = "Changement de mot de passe"; int main(argc,argv) int argc; char *argv[]; { int c, delai, flags, i, pwlen, uid; int filedes, erreur; long salt; char saltc[2]; char adresse[MAXLINELEN]; char commande[MAXLINELEN]; char from[MAXLINELEN]; char name[MAXLINELEN]; char newpass[MAXLINELEN]; char oldpass[MAXLINELEN]; char texte[MAXLINELEN]; char *_crypt(), *newpcrypt, *oldpcrypt; char *p, *pa, *progname, *temps; FILE *fichier; struct passwd *pwd; struct timeval tp; struct timezone tzp; #ifdef HAS_SHADOW FILE *fichiersh; struct spwd *shpwd; #endif progname = argv[0]; freopen(errfile, "a", stderr); if (gettimeofday(&tp, &tzp)) perror("heure"); temps = ctime(&tp); temps[24] = ' '; /* recherche du From: */ do { p = gets(texte); if ((strlen(texte) == 0) || (p == NULL)) { fprintf(stderr, "%s %s\n", temps, MNOFROM); return(1); } i = sscanf(texte,"%s",from); } while (strcmp(from,"From:")); /* lecture de l'adresse de l'expediteur : on saute un mot (From:), on saute un espace, puis on lit jusqu'en fin de ligne, on saute la fin de ligne. Remerciements a M. Burkhard (SLIG) pour l'argument de ce sscanf. */ i = sscanf(texte,"%*s%*c%[^\n]\n",adresse); /* nettoyage de l'adresse */ strcpy(texte, adresse); pa = adresse; if (p = index(texte, '<')) { /* il y a une adresse entre < > */ p++; while ((*pa++ = *p++) != '>'); *--pa = NULL; } else { /* suppression des commentaires */ p = texte; while (c = *p++) { if (c == '(') while ((c = *p++) != ')'); else *pa++ = c; } *pa = NULL; } sprintf(commande, "/usr/ucb/mail -s '%s' %s",sujet,adresse); if ((ppipe = popen(commande, "w")) == NULL) { fprintf(stderr, "%s %s", temps, MMAILUNAV); return(1); } /* positionnement apres la premiere ligne vide */ do { if (gets(texte) == NULL) {; fprintf(ppipe, "%s\n", MMAILEMPTY); termin(0); } } while (strlen(texte) != 0); /* lecture des trois mots */ i = scanf("%s%s%s",name,oldpass,newpass); if (i == EOF) { /* le message est vide */ fprintf(ppipe, "%s\n", MMAILEMPTY); termin(0); } if (i != 3) { /* il y a un probleme */ if (i == 1) fprintf(ppipe, "%s %s\n", MONEWORD, name); else fprintf(ppipe, "%s %s & %s\n", MTWOWORD, name, oldpass); termin(0); } /* verification que user existe */ pwd = getpwnam(name); #ifdef HAS_SHADOW shpwd = getspnam(name); if ((pwd == NULL) || (shpwd == NULL)) { #else if (pwd == NULL) { #endif fprintf(ppipe, "%s %s%s %s%s\n", MLOGIN, name, MUNKNOW, oldpass, MINCORR); termin(0); } /* verifions que le old password est correct */ #ifdef HAS_SHADOW p = shpwd -> sp_pwdp; #else p = pwd -> pw_passwd; #endif oldpcrypt = _crypt(oldpass,p); if (strcmp(oldpcrypt,p) != 0) { fprintf(ppipe, "%s %s%s %s%s\n", MLOGIN, name, MUNKNOW, oldpass, MINCORR); termin(0); } /* verification que le nouveau mot de passe est acceptable */ flags = 0; pwlen = strlen(newpass); p = newpass; while (c = *p++) { if (c >= 'a' && c <= 'z') flags |= 2; else if (c >= 'A' && c <= 'Z') flags |= 4; else if (c >= '0' && c <= '9') flags |= 1; else flags |= 8; } if (!( ((flags >= 7 && pwlen >= 4)) || (((flags == 2 || flags == 4) && pwlen >= 6)) || (((flags == 3 || flags == 5 || flags == 6) && pwlen >= 5)) )) { fprintf(ppipe, "%s %s, %s ", MNEWPASS, newpass, MBADBCAUSE); if (flags == 1) fprintf(ppipe, "%s\n", MPASSNMBER); else fprintf(ppipe, "%s\n", MPASSIMPLE); termin(0); } /* encryptage du nouveau mot de passe */ (void) time(&salt); salt = 9 * getpid(); saltc[0] = salt & 077; saltc[1] = (salt>>6) & 077; for (i = 0; i < 2; i++) { c = saltc[i] + '.'; if (c > '9') c += 7; if (c > 'Z') c += 6; saltc[i] = c; } newpcrypt = _crypt(newpass, saltc); /* verification que le user-uid est bien mis */ uid = geteuid(); if (uid != 0) { fprintf(stderr, "%s uid = %d. ", temps, uid); fprintf(stderr, "%s: permission denied.\n", progname); fprintf(ppipe, "%s\n", MSYSTEMERR); termin(0); } /* creation du fichier temporaire */ (void) umask(0); delai = 1; do { filedes = open(PASSTEMP, O_WRONLY|O_CREAT|O_EXCL, 0644); if (filedes < 0) { erreur = errno; if (erreur == EEXIST) { delai += delai; /* if (delai < MAXDELAI) sleep(delai); */ if (delai < MAXDELAI) { fprintf(stderr, "%s %s %s ", temps, PASSTEMP, MBUSYWAIT); fprintf(stderr, "%d s.\n", delai); sleep(delai); } else { fprintf(ppipe, "%s\n", MPWDBUSY); fprintf(stderr, "%s %s %s\n", temps, PASSTEMP, MALREADYTHERE); termin(0); } } else { fprintf(stderr, "%s ", temps); errno = erreur; perror(PASSTEMP); termin(0); } } } while (filedes < 0); if ((fichier = fdopen(filedes, "w")) == NULL) { fprintf(ppipe, "%s\n", MOPENPWDPB); fprintf(stderr, "%s %s %s.\n", temps, MOPENFAILED, PASSTEMP); termin(0); } #ifdef HAS_SHADOW delai = 1; /* sec */ do { filedes = open(SHADTEMP, O_WRONLY|O_CREAT|O_EXCL, 0644); if (filedes < 0) { erreur = errno; if (erreur == EEXIST) { delai += delai; /* if (delai < MAXDELAI) sleep(delai); */ if (delai < MAXDELAI) { fprintf(stderr, "%s %s %s ", temps, SHADTEMP, MBUSYWAIT); fprintf(stderr, "%d s.\n", delai); sleep(delai); } else { fprintf(ppipe, "%s\n", MPWDBUSY); fprintf(stderr, "%s %s %s\n", temps, SHADTEMP, MALREADYTHERE); termin(0); } } else { fprintf(stderr, "%s ", temps); errno = erreur; perror(SHADTEMP); exit(0); } } } while (filedes < 0); if ((fichiersh = fdopen(filedes, "w")) == NULL) { fprintf(ppipe, "%s\n", MOPENPWDPB); fprintf(stderr, "%s %s %s.\n", temps, MOPENFAILED, SHADTEMP); termin(0); } /* copie de passwd vers le temporaire et remplacement de la ligne avec le nouveau mot de passe */ while ((shpwd = getspent()) != NULL) { if (strcmp(shpwd->sp_namp, name) == 0) shpwd->sp_pwdp = newpcrypt; putspent(shpwd, fichiersh); } endspent(); (void) fflush(fichiersh); if (ferror(fichiersh)) { /* fsync?? */ fprintf(stderr, "%s Warning: %s write error, %s not updated\n", temps, SHADTEMP, SHADOW); #else /* copie de passwd vers le temporaire et remplacement de la ligne avec le nouveau mot de passe */ while ((pwd = getpwent()) != NULL) { if (strcmp(pwd->pw_name, name) == 0) pwd->pw_passwd = newpcrypt; putpwent(pwd, fichier); } endpwent(); (void) fflush(fichier); if (ferror(fichier)) { /* fsync?? */ fprintf(stderr, "%s Warning: %s write error, %s not updated\n", temps, PASSTEMP, PASSWD); #endif fprintf(ppipe, "%s\n", MPASSNOTUPDTD); goto out; } (void) fclose(fichier); #ifdef HAS_SHADOW (void) fclose(fichiersh); (void) unlink(PASSTEMP); if (rename(SHADTEMP, SHADOW) < 0) { #else if (rename(PASSTEMP, PASSWD) < 0) { #endif erreur = errno; fprintf(ppipe, "%s\n", MPASSPHPSUPDTD); (void) fprintf(stderr, "%s %s", temps, progname); errno = erreur; perror(": rename"); out: #ifdef HAS_SHADOW (void) unlink(SHADTEMP); #else (void) unlink(PASSTEMP); #endif termin(0); } fprintf(stderr, "%s %s %s%s %s.\n", temps, MPASSWDOF, name, MMODIFDBY, adresse); fprintf(ppipe, "%s %s %s %s %s.\n", MTHEPASSWDOF, name, MHASBNMODIFD, MBYREQUESTOF, adresse); termin(0); } /* * ferme le tube de mail et quitte. */ termin(etat) int etat; { pclose(ppipe); exit(etat); }