pengo@tmpmbx.UUCP (Hans H. Huebner) (11/19/88)
Hello, here is my shadow password file library. It should be System V R3.2 compatible. I haven't seen the original manual pages, thus there might be some discrepancies. Please let me know if you have compatibility suggestions. I have written and tested this on XENIX System V. There is a raw XENIX Makefile included. There should be no problems in getting this to run on BSD systems. Correct me, if I'm wrong. Thanks, Hans #! /bin/sh # This is a shell archive, meaning: # 1. Remove everything above the #! /bin/sh line. # 2. Save the resulting text in a file. # 3. Execute the file with /bin/sh (not csh) to create the files: # README # Article # Makefile # Makefile.xenix # shadow.h # getsp.c # getspent.c # fgetspent.c # putspent.c # getspnam.c # pwconv.c # pwunconv.c # getspent.3 # putspent.3 # pwconv.8 # shadow.4 # This archive created: Fri Nov 18 17:43:09 1988 export PATH; PATH=/bin:$PATH if test -f 'README' then echo shar: will not over-write existing file "'README'" else cat << \SHAR_EOF > 'README' This is the shadow password file library as desribed by Dennis Mumaugh in comp.unix.wizards recently. It should work similar to the AT&T version used in Sys V3.2. If you detect any discrepancies, please tell me. Sorry, no manual pages yet. I'll do them later (maybe ;-) ). Also included are the two program "pwconf" and "pwunconf" with the functionality described by Dennis. You'll find his description in the file "Article". Have fun, Hans -- Hans H. Huebner, netmbx | PSIMail: PSI%026245300043100::PENGO Woerther Str. 36 | DOMAIN: pengo@garp.mit.edu D-1000 Berlin 20, W.Germany | Bang: ..!{pyramid,unido}!tmpmbx!pengo Phone: (+49 30) 882 54 29 | BITNET: huebner@db0tui6 SHAR_EOF fi # end of overwriting check if test -f 'Article' then echo shar: will not over-write existing file "'Article'" else cat << \SHAR_EOF > 'Article' From tub!unido!mcvax!uunet!attcan!utgpu!watmath!clyde!att!cuuxb!dlm Mon Nov 14 00:16:59 MET 1988 Path: tmpmbx!tub!unido!mcvax!uunet!attcan!utgpu!watmath!clyde!att!cuuxb!dlm >From: dlm@cuuxb.ATT.COM (Dennis L. Mumaugh) Newsgroups: comp.unix.wizards Subject: /etc/shadow Summary: See release notes for SVR3.2 Keywords: shadow password Message-ID: <2189@cuuxb.ATT.COM> Date: 11 Nov 88 21:33:37 GMT References: <16722@agate.BERKELEY.EDU> <2178@cuuxb.ATT.COM> <16768@agate.BERKELEY.EDU> <17828@glacier.STANFORD.EDU> <2182@cuuxb.ATT.COM> <8 Reply-To: dlm@cuuxb.UUCP (Dennis L. Mumaugh) Organization: ATT Data Systems Group, Lisle, Ill. Lines: 60 Posted: Fri Nov 11 22:33:37 1988 In article <8861@smoke.BRL.MIL> gwyn@brl.arpa (Doug Gwyn (VLD/VMB) <gwyn>) writes: >It would be a great service to the community if specifications for >this feature were posted or at least sent to developers who want >to enable a similar feature on their (typically BSD-based) systems. >For example, what is the shadow file called, what is its format, >what sort of stuff is left in the password field in /etc/passwd, >what facilities are there to validate a password against the >shadow encrypted password file? The documentation is scattered in the Release Notes for System V Release 3.2. Of course they don't have a page shadow(4) but: The file is /etc/shadow and is owned by root and mode 400. It contains one line per login. Fields are separated by colons: username \- users login name password \- A 13 character encrypted password or a lock string to indicater the login is not accessible lastchanged \- number of days since January 1, 1970 that the password has been modified min \- the number of days required between password changes max \- the maximum number of days the password is valid. Routines to work with /etc/shadow: #include <shadow.h> struct spwd *getspent(); struct spwd *getspnam(char * name); void setspent(); void endspent(); struct spwd *fgetspent(FILE *fp); int putspent(struct spwd *p,FILE *fp); Programs allied with this are pwconv \- install and/or update /etc/shadow with information from /etc/passwd pwunconv \- restore /etc/password from /etc/shadown Programs like login, su and passwd work with either /etc/passwd ONLY or with the added /etc/shadow. If there is no entry in /etc/shadow we accept the /etc/passwd as gospel [in case someone forgot to run /usr/lib/pwconv after adding a user.] Also /usr/include/shadow.h: struct spwd { char *sp_namp; /* users login name */ char *sp_pwdp; /* encrypted password */ long sp_lstchg; /* number of days since January 1, 1970 that the password has been modified */ int sp_max; /* the number of days required between password changes */ int sp_min; /* the maximum number of days the password is valid. */ } #define SHADOW "/etc/shadow" ATT doesn't provide any of the functions or the header file as part of its product. It is in the source but not the binary. Thus developers who need the routines must contact their ATT person [not me!] to obtain the shadow password security library -- =Dennis L. Mumaugh Lisle, IL ...!{att,lll-crg}!cuuxb!dlm OR cuuxb!dlm@arpa.att.com SHAR_EOF fi # end of overwriting check if test -f 'Makefile' then echo shar: will not over-write existing file "'Makefile'" else cat << \SHAR_EOF > 'Makefile' # Makefile for the shadow password file library SHELL= /bin/sh # uncomment next line for Berserkeley systems #DEFINES= -Dstrchr=index # Compiler configuration CFLAGS= -I. -g $(DEFINES) LINTFLAGS= -I. LDFLAGS= -g -i SHAR= shar # File definition LIB= libshadow.a PROGRAMS= pwconv pwunconv LIBOBJ= $(LIB)(fgetspent.o) $(LIB)(getsp.o) \ $(LIB)(getspent.o) $(LIB)(getspnam.o) \ $(LIB)(putspent.o) SOURCES= getsp.c getspent.c fgetspent.c putspent.c getspnam.c PSOURCES= pwconv.c pwunconv.c HFILES= shadow.h MISCFILES= README Article Makefile Makefile.xenix MANUALS= getspent.3 putspent.3 pwconv.8 shadow.4 all: $(PROGRAMS) $(LIB) pwunconv: pwunconv.o $(LIB) cc -o pwunconv $(LDFLAGS) pwunconv.o $(LIB) pwconv: pwconv.o $(LIB) cc -o pwconv $(LDFLAGS) pwconv.o $(LIB) $(LIB): ranlib $(LIB) lint: lint $(LINTFLAGS) $(SOURCES) tags: $(SOURCES) $(HFILES) ctags $(SOURCES) $(HFILES) clean: rm -f *.o $(LIB) $(PROGRAMS) shadow.sh: $(MISCFILES) $(HFILES) $(SOURCES) $(PSOURCES) $(MANUALS) $(SHAR) $(MISCFILES) $(HFILES) $(SOURCES) $(PSOURCES) $(MANUALS) > shadow.sh $(LIB): $(LIBOBJ) $(LIBOBJ): $(HFILES) pwunconv.o pwconv.o: $(HFILES) SHAR_EOF fi # end of overwriting check if test -f 'Makefile.xenix' then echo shar: will not over-write existing file "'Makefile.xenix'" else cat << \SHAR_EOF > 'Makefile.xenix' # ex:set ts=8 sw=8: CFLAGS= -I. -g $(CMODEL) LINTFLAGS= -I. LDFLAGS= -g -i RANLIB= ranlib LIB= libshadow.a LIBOBJ= $(MODEL)$(LIB)(fgetspent.o) $(MODEL)$(LIB)(getsp.o) \ $(MODEL)$(LIB)(getspent.o) $(MODEL)$(LIB)(getspnam.o) \ $(MODEL)$(LIB)(putspent.o) SOURCES= getsp.c getspent.c fgetspent.c putspent.c getspnam.c HFILES= shadow.h MISCFILES= Makefile all: small pwconv pwunconv pwunconv: pwunconv.o Slibshadow.a cc -o pwunconv $(LDFLAGS) pwunconv.o Slibshadow.a pwconv: pwconv.o Slibshadow.a cc -o pwconv $(LDFLAGS) pwconv.o Slibshadow.a small: make lib MODEL=S CMODEL=-Mes2 middle: make lib MODEL=M CMODEL=-Mem2 large: make lib MODEL=L CMODEL=-Mel2 lib: $(MODEL)$(LIB) $(RANLIB) $(MODEL)$(LIB) lint: lint $(LINTFLAGS) $(SOURCES) install: large middle small cp ?$(LIB) /usr/lib chmod +r /usr/lib/?$(LIB) cp $(HFILES) /usr/include tags: $(SOURCES) $(HFILES) ctags $(SOURCES) $(HFILES) clean: rm -f *.o ?$(LIB) tar: clean $(MODEL)$(LIB): $(LIBOBJ) $(LIBOBJ): $(HFILES) pwconv.o: $(HFILES) SHAR_EOF fi # end of overwriting check if test -f 'shadow.h' then echo shar: will not over-write existing file "'shadow.h'" else cat << \SHAR_EOF > 'shadow.h' /* shadow.h Shadow passwd stuff */ struct spwd { char *sp_namp; /* users login name */ char *sp_pwdp; /* encrypted password */ long sp_lstchg; /* number of days since January 1, 1970 that the password has been modified */ int sp_max; /* the number of days required between password changes */ int sp_min; /* the maximum number of days the password is valid. */ }; #define SHADOW "/etc/shadow" extern struct spwd *getspent(); extern struct spwd *getspnam(); extern void setspent(); extern void endspent(); extern struct spwd *fgetspent(); extern int putspent(); SHAR_EOF fi # end of overwriting check if test -f 'getsp.c' then echo shar: will not over-write existing file "'getsp.c'" else cat << \SHAR_EOF > 'getsp.c' /* * getsp.c Shadow passwd file -- common functions * * Written by Hans Huebner, netmbx Berlin (pengo@tmpmbx.UUCP) * * This code may be freely distributed, provided this copyright notice * remains intact. * */ #include <stdio.h> #include <shadow.h> FILE *_spfile = NULL; /* * setspent() searches start position of shadow passwd file * */ void setspent() { extern FILE *fopen(); if (!_spfile) { _spfile = fopen(SHADOW, "r"); } else { (void) fseek(_spfile, 0l, 0); } } /* * endspent() close shadow passwd file * */ void endspent() { if (_spfile) { (void) fclose(_spfile); } } SHAR_EOF fi # end of overwriting check if test -f 'getspent.c' then echo shar: will not over-write existing file "'getspent.c'" else cat << \SHAR_EOF > 'getspent.c' /* * getspent.c Shadow passwd file -- get next entry * * Written by Hans Huebner, netmbx Berlin (pengo@tmpmbx.UUCP) * * This code may be freely distributed, provided this copyright notice * remains intact. * */ #include <stdio.h> #include <shadow.h> /* * getspent() get next entry of the shadow passwd file * * returns a pointer to the spwd entry read, or NULL * upon end of file or error. * */ struct spwd * getspent() { extern FILE *_spfile; if (!_spfile) setspent(); if (_spfile) return(fgetspent(_spfile)); else return(NULL); } SHAR_EOF fi # end of overwriting check if test -f 'fgetspent.c' then echo shar: will not over-write existing file "'fgetspent.c'" else cat << \SHAR_EOF > 'fgetspent.c' /* * fgetspent.c Shadow passwd file -- get spwd entry from a given * file * * Written by Hans Huebner, netmbx Berlin (pengo@tmpmbx.UUCP) * * This code may be freely distributed, provided this copyright notice * remains intact. * */ #include <stdio.h> #include <shadow.h> /* * fgetspent() get next spwd entry from a given file, returns * pointer to spwd upon success, NULL on error or * end of file. * */ #define SBUF 255 /* max. line length */ struct spwd * fgetspent(fp) FILE *fp; { static char _buf[SBUF]; static struct spwd _retval; extern char *strchr(); char *colon; if (!fgets(_buf, sizeof(_buf), fp)) return(NULL); _retval.sp_namp = _buf; if (!(colon = strchr(_buf, ':'))) return(NULL); /* incorrect file format */ else *colon++ = '\0'; _retval.sp_pwdp = colon; if (!(colon = strchr(colon, ':'))) return(NULL); /* incorrect file format */ else *colon++ = '\0'; if (sscanf(colon, "%ld:%d:%d", &_retval.sp_lstchg, &_retval.sp_max, &_retval.sp_min) != 3) return(NULL); /* incorrect file format */ else return(&_retval); } SHAR_EOF fi # end of overwriting check if test -f 'putspent.c' then echo shar: will not over-write existing file "'putspent.c'" else cat << \SHAR_EOF > 'putspent.c' /* * putspent.c Shadow passwd file -- put an entry to the shadow * passwd file. * * Written by Hans Huebner, netmbx Berlin (pengo@tmpmbx.UUCP) * * This code may be freely distributed, provided this copyright notice * remains intact. * */ #include <stdio.h> #include <shadow.h> /* * putspent() put an entry to the shadow passwd file * * returns -1 on error, 0 on success * */ int putspent(sp, fp) struct spwd *sp; FILE *fp; { if (!fp) return(-1); /* you never know */ return(fprintf(fp, "%s:%s:%ld:%d:%d\n", sp->sp_namp, sp->sp_pwdp, sp->sp_lstchg, sp->sp_max, sp->sp_min) > 0 ? 0 : -1); } SHAR_EOF fi # end of overwriting check if test -f 'getspnam.c' then echo shar: will not over-write existing file "'getspnam.c'" else cat << \SHAR_EOF > 'getspnam.c' /* * getspnam.c Shadow passwd file -- get spwd entry by name * * Written by Hans Huebner, netmbx Berlin (pengo@tmpmbx.UUCP) * * This code may be freely distributed, provided this copyright notice * remains intact. * */ #include <stdio.h> #include <shadow.h> /* * getspnam() get the spwd entry for the user given as argument * * returns a pointer to the spwd entry, or NULL if * the user wasn't found or an error was encountered. * */ struct spwd * getspnam(name) char *name; { struct spwd *sp; setspent(); while (sp = getspent()) if (!strcmp(sp->sp_namp, name)) return(sp); return(NULL); } SHAR_EOF fi # end of overwriting check if test -f 'pwconv.c' then echo shar: will not over-write existing file "'pwconv.c'" else cat << \SHAR_EOF > 'pwconv.c' /* * pconv.c Shadow passwd file -- convert /etc/passwd to /etc/shadow * * Written by Hans Huebner, netmbx Berlin (pengo@tmpmbx.UUCP) * * This code may be freely distributed, provided this copyright notice * remains intact. * */ #include <stdio.h> #include <shadow.h> #include <pwd.h> extern struct passwd *getpwent(); extern FILE *fopen(); extern char *strchr(); extern struct spwd *getspnam(); char *agecode = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; #define weeks(code) ((int) (strchr(agecode, code) - agecode)) main() { FILE *out; struct passwd *pw; struct spwd spwbuf; struct spwd *spw; umask(0277); if (!(out = fopen(SHADOW, "a"))) { perror(SHADOW); exit(1); } setpwent(); while (pw = getpwent()) { if (!(spw = getspnam(pw->pw_name))) { char *age = pw->pw_age ? pw->pw_age : ""; spwbuf.sp_namp = pw->pw_name; spwbuf.sp_pwdp = pw->pw_passwd; spwbuf.sp_max = spwbuf.sp_min = 0; spwbuf.sp_lstchg = 0l; if (*age) spwbuf.sp_max = weeks(*age++); if (*age) spwbuf.sp_min = weeks(*age++); if (*age) spwbuf.sp_lstchg = (long) weeks(age[0]) + 64l * (long) weeks(age[1]); if (putspent(&spwbuf, out) == -1) { perror("write error"); exit(1); } } } endpwent(); fclose(out); } SHAR_EOF fi # end of overwriting check if test -f 'pwunconv.c' then echo shar: will not over-write existing file "'pwunconv.c'" else cat << \SHAR_EOF > 'pwunconv.c' /* * pwunconv.c Shadow passwd file -- convert /etc/shadow to /etc/passwd * * Written by Hans Huebner, netmbx Berlin (pengo@tmpmbx.UUCP) * * This code may be freely distributed, provided this copyright notice * remains intact. * * BUGS: no file locking is supported. * */ #include <stdio.h> #include <shadow.h> #include <pwd.h> #define TEMPFILE "/etc/passwd.new" #define OLDFILE "/etc/passwd.old" #define PASSWD "/etc/passwd" char *agecode = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"; extern struct passwd *getpwent(); extern struct spwd *getspnam(); main() { FILE *out; struct passwd *pw; struct spwd *spw; char age[5]; if (!(out = fopen(TEMPFILE, "w"))) { perror(TEMPFILE); exit(1); } setpwent(); while (pw = getpwent()) { if (spw = getspnam(pw->pw_name)) { pw->pw_passwd = spw->sp_pwdp; if (spw->sp_max) { pw->pw_age = age; age[0] = agecode[spw->sp_max]; age[1] = agecode[spw->sp_min]; if (spw->sp_lstchg) { age[2] = agecode[spw->sp_lstchg % 64]; age[3] = agecode[spw->sp_lstchg / 64]; age[4] = '\0'; } else age[2] = '\0'; } } if (putpwent(pw, out)) { perror("write error"); exit(1); } } endpwent(); fclose(out); (void) unlink(OLDFILE); if (link(PASSWD, OLDFILE) == -1) { fprintf(stderr, "cannot link %s to %s", PASSWD, OLDFILE); perror(""); exit(1); } if (unlink(PASSWD) == -1) { fprintf(stderr, "cannot unlink %s", PASSWD); perror(""); exit(1); } if (link(TEMPFILE, PASSWD) == -1) { fprintf(stderr, "cannot link %s to %s", TEMPFILE, PASSWD); perror(""); (void) link(OLDFILE, PASSWD); exit(1); } (void) unlink(TEMPFILE); } SHAR_EOF fi # end of overwriting check if test -f 'getspent.3' then echo shar: will not over-write existing file "'getspent.3'" else cat << \SHAR_EOF > 'getspent.3' .TH GETSPENT 3 LOCAL .SH NAME getspent, getspnam, setspent, endspent \- Gets shadow password file entry .SH SYNOPSIS .B "#include <shadow.h>" .P .B "struct spwd *getspent()" .P .B "struct spwd *getspnam()" .P .B "int setspent()" .P .B "int endspent()" .SH DESCRIPTION .I getspent, fgetspent and .I getspnam each returns a pointer to a structure containing a shadow password entry. The structure of these entries is described in /usr/include/shadow.h. .P The meaning of the fields are described in shadow(4). .P .I getspent and .I fgetspent read the next entry in the file, so successive calls may be used to search through the entire file. .I fgetspent allows to specify a file opened by the calling routine. .I getspent searches from the beginning of the file until a matching user name is found or EOF is encountered. .P .I setspent rewinds the shadow password file. .I endspent may be called to close the file. .SH FILES /etc/shadow .SH "SEE ALSO" putspent(3), shadow(4) .SH DIAGNOSTICS .I getspent, fgetspent and .I getspnam return a pointer to the record read, or NULL if an error or EOF has been encountered. .SH NOTES The pointers returned point to static data so it must be copied by the calling program if it is to be saved. SHAR_EOF fi # end of overwriting check if test -f 'putspent.3' then echo shar: will not over-write existing file "'putspent.3'" else cat << \SHAR_EOF > 'putspent.3' .TH PUTSPENT 3 LOCAL .SH NAME putspent \- Writes shadow password file entry .SH SYNOPSIS .B "#include <shadow.h>" .P .B "int putspent(*spwd, *fp)" .br .B "struct spwd *spwd;" .br .B "FILE *fp" .SH DESCRIPTION .I putspent is the inverse of getspent(3). It puts the shadow password structure pointed to by spwd to the stream fp. The line matches the format of /etc/shadow. .SH FILES /etc/shadow .SH "SEE ALSO" getspent(3), shadow(4) .SH DIAGNOSTICS .I putspent returns 0 on successful completion, nonzero upon error. SHAR_EOF fi # end of overwriting check if test -f 'pwconv.8' then echo shar: will not over-write existing file "'pwconv.8'" else cat << \SHAR_EOF > 'pwconv.8' .TH PWCONV 8 LOCAL .SH NAME pwconv, pwunconv \- convert password entries between /etc/passwd and /etc/shadow .SH SYNOPSIS .B "/usr/lib/pwconv" .P .B "/usr/lib/pwunconv" .SH DESCRIPTION .I pwconv converts password entries from /etc/passwd to /etc/shadow. For all login names which are found in /etc/passwd but have no entry in /etc/shadow, a new entry is created. This is used to upgrade systems to a higher security level or to create an entry in /etc/shadow for a newsly created account. .P .I pwunconv converts entries in /etc/shadow back into /etc/passwd. It's not normally useful. .SH FILES /etc/shadow .br /etc/passwd .SH "SEE ALSO" shadow(4) .SH DIAGNOSTICS .I pwconv and .I pwunconv exit, if they have no permissions to access /etc/passwd and /etc/shadow. SHAR_EOF fi # end of overwriting check if test -f 'shadow.4' then echo shar: will not over-write existing file "'shadow.4'" else cat << \SHAR_EOF > 'shadow.4' .TH SHADOW 4 LOCAL .SH NAME shadow \- The shadow password file. .SH DESCRIPTION .I shadow contains password information for users of the system. This file is not world-readable, and as such it improves the systems' security a bit. .P Each line in the shadow file contain the following information: .P \-Login name - The users' login name. .P \-Password - The 13 character encrypted password. .P \-Last change - Number of days since January 1. 1970 that the password has been modified. .P \-Minimum change period \- The number of days required between password changes. .P \-Maximum change period \- The maximum number of days th password is valid. .SH FILES /etc/shadow .SH "SEE ALSO" getspent(3), putspent(3), pwconv(8) SHAR_EOF fi # end of overwriting check # End of shell archive exit 0
dlm@cuuxb.ATT.COM (Dennis L. Mumaugh) (11/29/88)
In article <1358@tmpmbx.UUCP> pengo@tmpmbx.UUCP (Hans H. Huebner) writes:
Hello,
here is my shadow password file library. It should be System V R3.2
compatible. I haven't seen the original manual pages, thus there might be
some discrepancies. Please let me know if you have compatibility
suggestions.
I have written and tested this on XENIX System V. There is a raw XENIX
Makefile included. There should be no problems in getting this to run on
BSD systems. Correct me, if I'm wrong.
Thanks,
Hans
Please see my article <2233@cuuxb.ATT.COM> for a small fix to
shadow.h: Change all ints in the spwd struct to long.
--
=Dennis L. Mumaugh
Lisle, IL ...!{att,lll-crg}!cuuxb!dlm OR cuuxb!dlm@arpa.att.com