|
|
The prwarn.c program warns users when their passwords are about to expire. The prwarn command takes three optional arguments. The arguments specify the number of days before a password expires that a warning is given, the number of hours and minutes between warnings, and the user name(s) who receives the warning. The logged-in user must be authorized to change the passwords of all specified users.
The program is usually run from a user's .profile or .login file, in which case no user names are specified and the warning applies to this user. The time of the warning is saved as the modification time of the $HOME/.prwarn_time file.
This program demonstrates how to:
Any program that queries the Protected Password database
must be executed with the effective group ID
(EUID) of auth.
To install this program, make the binary
setgid auth. For example, use
the following commands:
chgrp auth prwarn
chmod 2111 prwarn
Compile this program with the following command:
cc prwarn.c -lprot -lx -o prwarn
The -lprot option searches the prot library that contains the trusted routines.
This program is structured so that the subroutines are defined in front of the main routine, which begins in line 291.
1 #define SecureWare /* enable security features */2 #include <sys/types.h> /* system pseudo-types */ 3 #include <stdio.h> /* standard I/O definitions */ 4 #include <string.h> /* string library definitions */ 5 #include <stdarg.h> /* variable-len argument lists */ 6 #include <unistd.h> /* access(S) definitions */ 7 #include <sys/security.h> /* various security definitions */ 8 #include <sys/audit.h> /* audit definitions */ 9 #include <prot.h> /* database definitions */ 10 #include <pwd.h> /* /etc/passwd definitions */ 11 #include <time.h> /* localtime(S) definitions */ 12 #include <utime.h> /* utime(S) definitions */ 13 #include <sys/stat.h> /* stat(S) definitions */
14 extern int defopen(char *); 15 extern char *defread(char *);
#define SecureWare
statement is required.
This allows the security-related definitions in the
header files to be interpreted correctly.
prwarn.c (Part 2/13)
16 #define TRUE 1 17 #define FALSE 0 18 #define ARBSTRSIZ 300 /* ARBitrary STRing SIZe*/ 19 #define SECONDS_PER_HOUR (60L * 60L) 20 #define SECONDS_PER_DAY (24L * SECONDS_PER_HOUR) 21 time_t now; 22 int is_administrator; /* TRUE if LUID is Accounts Administrator */23 char prwarn_file[] = ".prwarn_time"; 24 char default_file[] = "/etc/default/prwarn";
25 time_t silence = 6 * SECONDS_PER_HOUR; 26 int always = FALSE; 27 int 28 set_remind_time( 29 char *str 30 ) { 31 int hhmm, hour, mins;
32 if (strcmp(str, "always") == 0) { 33 always = TRUE; 34 return (0); 35 } 36 hhmm = atoi(str); 37 if (strlen(str) < 2) { 38 hour = hhmm; 39 mins = 0; 40 } 41 else { 42 hour = hhmm / 100; 43 mins = hhmm % 100; 44 } 45 if (hour < 0 || mins < 0 || 59 < mins) 46 return (-1); 47 silence = (((time_t)hour * 60L) + (time_t)mins) * 60L; 48 always = FALSE; 49 return (0); 50 }
#define
statements are shown here for simplicity,
but in an application with several programs
it might make sense to put these definitions
in an application header file
that would use #include
statements
in all programs for the application.
prwarn.c (Part 3/13)
51 time_t forced = 7 * SECONDS_PER_DAY; /* -d (time before expires) */ 52 int nolimit = FALSE; /* TRUE if no -d cutoff time */
53 int 54 set_time_before( 55 char *str 56 ) { 57 int days;
58 if (strncmp(str, "inf", 3) == 0) { 59 nolimit = TRUE; 60 return (0); 61 } 62 if ((days = atoi(str)) < 0) 63 return (-1);
64 forced = (time_t)days * SECONDS_PER_DAY; 65 nolimit = FALSE; 66 return (0); 67 }
68 int 69 days_in_year( 70 register int yr 71 ) { 72 if (((yr % 4) == 0 && (yr % 100) != 0) || (yr % 400) == 0) 73 return (366); 74 return (365); 75 }
prwarn.c (Part 4/13)
76 char * 77 when( 78 time_t then 79 ) { 80 struct tm today, other; 81 long days;82 static char buf[ARBSTRSIZ];
83 other = *localtime(&then); 84 today = *localtime(&now); 85 if (other.tm_year == today.tm_year+1) { /* next year */ 86 other.tm_yday += days_in_year(today.tm_year); 87 other.tm_year = today.tm_year; 88 } 89 else if (other.tm_year == today.tm_year-1) { /* last year */ 90 today.tm_yday += days_in_year(other.tm_year); 91 today.tm_year = other.tm_year; 92 } 93 if (other.tm_year == today.tm_year) { 94 if (other.tm_yday == today.tm_yday-1) 95 return ("yesterday"); 96 if (other.tm_yday == today.tm_yday) 97 return ("today"); 98 if (other.tm_yday == today.tm_yday+1) 99 return ("tomorrow"); 100 } 101 if (then > now) { 102 days = (then - now) / SECONDS_PER_DAY; 103 (void) sprintf(buf, "in %ld days", days); 104 } 105 else { 106 days = (now - then) / SECONDS_PER_DAY; 107 (void) sprintf(buf, "%ld days ago", days); 108 } 109 return (buf); 110 }
prwarn.c (Part 5/13)
111 time_t 112 last_reported( 113 struct passwd *pwd 114 ) { 115 struct stat stbuf; 116 time_t last;117 if (chdir("/")) 118 perror("Cannot change current working directory to /"); 119 else if (chdir(pwd->pw_dir) == 0 && stat(prwarn_file, &stbuf) == 0) 120 return (stbuf.st_mtime);
121 return (0); /* The Epoch: Jan 1st 1970 00:00 GMT */ 122 }
123 void 124 just_reported( 125 struct passwd *pwd 126 ) { 127 struct utimbuf utbuf;
128 if (eaccess(prwarn_file, F_OK) && close(creat(prwarn_file, 0600)) == 0) 129 (void) chown(prwarn_file, pwd->pw_uid, pwd->pw_gid); 130 utbuf.actime = now; 131 utbuf.modtime = now; 132 (void) utime(prwarn_file, &utbuf); 133 }
prwarn.c (Part 6/13)
134 time_t 135 expires_at( 136 register struct pr_passwd *pr_pw 137 ) { 138 if (pr_pw->uflg.fg_expire) 139 return (pr_pw->ufld.fd_expire); 140 else if (pr_pw->sflg.fg_expire) 141 return (pr_pw->sfld.fd_expire);
142 audit_security_failure( 143 OT_DFLT_CNTL, -1L, 0L, AUTH_DEFAULT, 144 "No system default password expiration time" 145 ); 146 return (0); 147 }
148 time_t 149 minchange_is( 150 register struct pr_passwd *pr_pw 151 ) { 152 if (pr_pw->uflg.fg_min) 153 return (pr_pw->ufld.fd_min); 154 else if (pr_pw->sflg.fg_min) 155 return (pr_pw->sfld.fd_min);
156 audit_security_failure( 157 OT_DFLT_CNTL, -1L, 0L, AUTH_DEFAULT, 158 "No system default minimum password change time" 159 ); 160 return (0); 161 }
prwarn.c (Part 7/13)
162 time_t 163 lives_until( 164 register struct pr_passwd *pr_pw 165 ) { 166 if (pr_pw->uflg.fg_lifetime) 167 return (pr_pw->ufld.fd_lifetime); 168 else if (pr_pw->sflg.fg_lifetime) 169 return (pr_pw->sfld.fd_lifetime);170 audit_security_failure( 171 OT_DFLT_CNTL, -1L, 0L, AUTH_DEFAULT, 172 "No system default password lifetime" 173 ); 174 return (0); 175 }
176 time_t 177 last_changed_at( 178 register struct pr_passwd *pr_pw 179 ) { 180 if (pr_pw->uflg.fg_schange) 181 return (pr_pw->ufld.fd_schange);
182 return (0); 183 }
prwarn.c (Part 8/13)
184 enum password_change { 185 CanBeChanged, 186 TooEarly, 187 Procrastinated 188 } changeable( 189 register struct pr_passwd *pr_pw 190 ) { 191 time_t lastchg, life;192 if ((lastchg = last_changed_at(pr_pw)) == 0) 193 return (CanBeChanged);
194 if (lastchg + minchange_is(pr_pw) > now) 195 return (TooEarly);
196 if ((life = lives_until(pr_pw)) != 0 && lastchg + life < now) 197 return (Procrastinated);
198 return (CanBeChanged); 199 }
200 int 201 authorized_to_change( 202 register struct pr_passwd *pr_pw 203 ) { 204 if (is_administrator) 205 return (TRUE); 206 else if (pr_pw->uflg.fg_pswduser) 207 return (pr_pw->ufld.fd_pswduser == starting_luid()); 208 else if (pr_pw->sflg.fg_pswduser) 209 return (pr_pw->sfld.fd_pswduser == starting_luid()); 210 else if (pr_pw->uflg.fg_uid) 211 return (pr_pw->ufld.fd_uid == starting_luid()); 212 return (FALSE); 213 }
prwarn.c (Part 9/13)
214 int 215 report( 216 struct passwd *pwd, 217 int do_update 218 ) { 219 struct pr_passwd *pr_pw; 220 time_t expire, lastchg, nextchg;221 if ((pr_pw = getprpwnam(pwd->pw_name)) == (struct pr_passwd *)0) { 222 audit_auth_entry(pwd->pw_name, OT_PRPWD, 223 "User missing from Protected Password database" 224 ); 225 (void) fprintf(stderr, 226 "%s: Missing Protected Password entry: %s\n", 227 command_name, pwd->pw_name 228 ); 229 return (1); 230 }
231 if ( ! authorized_to_change(pr_pw)) { 232 (void) fprintf(stderr, 233 "%s: Sorry, you are not authorized to change %s's password.\n", 234 command_name, pwd->pw_name 235 ); 236 return (1); 237 }
prwarn.c (Part 10/13)
238 if ( ! always && now - last_reported(pwd) < silence) 239 return (0);
240 expire = expires_at(pr_pw); 241 lastchg = last_changed_at(pr_pw); 242 nextchg = lastchg + expire; 243 if (lastchg == 0 || nolimit || (expire && nextchg - forced <= now)) { 244 if (do_update) 245 (void) fputs("Your password", stdout); 246 else 247 (void) printf("The password for %s", pwd->pw_name); 248 if (expire == 0) 249 (void) fputs(" never expires", stdout); 250 else if (lastchg == 0) 251 (void) fputs(" has never been set", stdout); 252 else 253 (void) printf(" expire%c %s at %s", 254 (nextchg < now) ? 'd' : 's', 255 when(nextchg), 256 nl_cxtime(&nextchg, "") 257 ); 258 switch (changeable(pr_pw)) { 259 case CanBeChanged: 260 (void) fputs(",\n\tand can be changed", stdout); 261 break; 262 case Procrastinated: 263 (void) fputs(",\n\tand is now dead", stdout); 264 break; 265 } 266 (void) fputs(".\n", stdout); 267 if (do_update) 268 just_reported(pwd); 269 } 270 return (0); 271 }
prwarn.c (Part 11/13)
272 void 273 usage( 274 char *fmt, 275 ... 276 ) { 277 va_list args;
278 if (fmt != (char *)0) { 279 (void) fprintf(stderr, "%s: ", command_name);
280 va_start(args, fmt); 281 (void) vfprintf(stderr, fmt, args); 282 va_end(args);
283 putc('\n', stderr); 284 } 285 (void) fprintf(stderr, 286 "Usage: %s [ -d days ] [ -t hh[mm] ] [ users ]...\n", 287 command_name 288 ); 289 exit(2); 290 /* NOTREACHED */ 291 }
prwarn.c (Part 12/13)
292 main( 293 int argc, 294 char *argv[] 295 ) { 296 int c, nerrs; 297 char *str, temp[ARBSTRSIZ]; 298 struct passwd *pwd;299 set_auth_parameters(argc, argv);
300 (void) time(&now); 301 is_administrator = authorized_user("auth");
302 if (defopen(default_file) == 0) { 303 if ((str = defread("DAYS_BEFORE=")) != (char *)0) 304 (void) set_time_before(str); 305 if ((str = defread("REMIND_TIME=")) != (char *)0) 306 (void) set_remind_time(str); 307 (void) defopen((char *)0); 308 }
309 while ((c = getopt(argc, argv, "d:t:")) != EOF) { 310 switch (c) { 311 case 'd': 312 if (set_time_before(optarg)) 313 usage("Invalid number of days: %s", optarg); 314 break; 315 case 't': 316 if (set_remind_time(optarg)) 317 usage("Invalid time interval: %s", optarg); 318 break; 319 case '?': 320 default: 321 usage((char *)0); 322 } 323 }
prwarn.c (Part 13/13)
324 if (optind >= argc) {
325 if ((pwd = getpwuid(starting_luid())) != (struct passwd *)0) 326 exit(report(pwd, TRUE)); 327 (void) sprintf(temp, "UID %d", starting_luid()); 328 audit_auth_entry(temp, OT_PWD, 329 "User missing from /etc/passwd" 330 ); 331 (void) fprintf(stderr, 332 "%s: Cannot determine your username!\n", 333 command_name 334 ); 335 exit(1); 336 } 337 else { 338 while (optind < argc) { 339 pwd = getpwnam(argv[optind]); 340 if (pwd != (struct passwd *)0) 341 nerrs += report(pwd, FALSE); 342 else 343 (void) printf("There is no user named %s.\n", 344 argv[optind] 345 ); 346 optind++; 347 } 348 exit(nerrs != 0); 349 } 350 /* NOTREACHED */ 351 }