1#!/usr/bin/perl -w 2#- 3# Copyright (c) 2002 Networks Associates Technology, Inc. 4# All rights reserved. 5# 6# This software was developed for the FreeBSD Project by ThinkSec AS and 7# NAI Labs, the Security Research Division of Network Associates, Inc. 8# under DARPA/SPAWAR contract N66001-01-C-8035 ("CBOSS"), as part of the 9# DARPA CHATS research program. 10# 11# Redistribution and use in source and binary forms, with or without 12# modification, are permitted provided that the following conditions 13# are met: 14# 1. Redistributions of source code must retain the above copyright 15# notice, this list of conditions and the following disclaimer. 16# 2. Redistributions in binary form must reproduce the above copyright 17# notice, this list of conditions and the following disclaimer in the 18# documentation and/or other materials provided with the distribution. 19# 3. The name of the author may not be used to endorse or promote 20# products derived from this software without specific prior written 21# permission. 22# 23# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 24# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 25# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 26# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 27# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 28# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 29# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 30# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 31# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 32# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 33# SUCH DAMAGE. 34# 35# $P4: //depot/projects/openpam/misc/gendoc.pl#10 $ 36# 37 38use strict; 39use Fcntl; 40use POSIX qw(strftime); 41use vars qw($COPYRIGHT $TODAY %FUNCTIONS %PAMERR); 42 43%PAMERR = ( 44 PAM_SUCCESS => "Success", 45 PAM_OPEN_ERR => "Failed to load module", 46 PAM_SYMBOL_ERR => "Invalid symbol", 47 PAM_SERVICE_ERR => "Error in service module", 48 PAM_SYSTEM_ERR => "System error", 49 PAM_BUF_ERR => "Memory buffer error", 50 PAM_CONV_ERR => "Conversation failure", 51 PAM_PERM_DENIED => "Permission denied", 52 PAM_MAXTRIES => "Maximum number of tries exceeded", 53 PAM_AUTH_ERR => "Authentication error", 54 PAM_NEW_AUTHTOK_REQD => "New authentication token required", 55 PAM_CRED_INSUFFICIENT => "Insufficient credentials", 56 PAM_AUTHINFO_UNAVAIL => "Authentication information is unavailable", 57 PAM_USER_UNKNOWN => "Unknown user", 58 PAM_CRED_UNAVAIL => "Failed to retrieve user credentials", 59 PAM_CRED_EXPIRED => "User credentials have expired", 60 PAM_CRED_ERR => "Failed to set user credentials", 61 PAM_ACCT_EXPIRED => "User accound has expired", 62 PAM_AUTHTOK_EXPIRED => "Password has expired", 63 PAM_SESSION_ERR => "Session failure", 64 PAM_AUTHTOK_ERR => "Authentication token failure", 65 PAM_AUTHTOK_RECOVERY_ERR => "Failed to recover old authentication token", 66 PAM_AUTHTOK_LOCK_BUSY => "Authentication token lock busy", 67 PAM_AUTHTOK_DISABLE_AGING => "Authentication token aging disabled", 68 PAM_NO_MODULE_DATA => "Module data not found", 69 PAM_IGNORE => "Ignore this module", 70 PAM_ABORT => "General failure", 71 PAM_TRY_AGAIN => "Try again", 72 PAM_MODULE_UNKNOWN => "Unknown module type", 73 PAM_DOMAIN_UNKNOWN => "Unknown authentication domain", 74); 75 76sub parse_source($) { 77 my $fn = shift; 78 79 local *FILE; 80 my $source; 81 my $func; 82 my $descr; 83 my $type; 84 my $args; 85 my $argnames; 86 my $man; 87 my $inlist; 88 my $inliteral; 89 my %xref; 90 my @errors; 91 92 if ($fn !~ m,\.c$,) { 93 warn("$fn: not C source, ignoring\n"); 94 return; 95 } 96 97 sysopen(FILE, $fn, O_RDONLY) 98 or die("$fn: open(): $!\n"); 99 $source = join('', <FILE>); 100 close(FILE); 101 102 return if ($source =~ m/^ \* NOPARSE\s*$/m); 103 104 if (!defined($COPYRIGHT) && $source =~ m,^(/\*-\n.*?)\s*\*/,s) { 105 $COPYRIGHT = $1; 106 $COPYRIGHT =~ s,^.\*,.\\\",gm; 107 $COPYRIGHT =~ s,(\$(?:)P4).*?\$,$1\$,; 108 $COPYRIGHT .= "\n.\\\""; 109 } 110 $func = $fn; 111 $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,; 112 if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) { 113 warn("$fn: can't find $func\n"); 114 return; 115 } 116 ($descr, $type, $args) = ($1, $2, $3); 117 $descr =~ s,^([A-Z][a-z]),lc($1),e; 118 $descr =~ s,[\.\s]*$,,; 119 while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) { 120 # nothing 121 } 122 $args =~ s/,\s+/, /gs; 123 $args = "\"$args\""; 124 125 %xref = ( 126 "pam 3" => 1 127 ); 128 129 if ($type eq "int") { 130 foreach (split("\n", $source)) { 131 next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/); 132 push(@errors, $1); 133 } 134 $xref{"pam_strerror 3"} = 1; 135 } 136 137 $argnames = $args; 138 $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g; 139 $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g; 140 $argnames =~ s/\" \"/|/g; 141 $argnames =~ s/^\"(.*)\"$/($1)/; 142 foreach (split("\n", $source)) { 143 s/\s*$//; 144 if (!defined($man)) { 145 if (m/^\/\*\*$/) { 146 $man = ""; 147 } 148 next; 149 } 150 last if (m/^ \*\/$/); 151 s/^ \* ?//; 152 s/\\(.)/$1/gs; 153 if (m/^$/) { 154 if ($man ne "" && $man !~ m/\.Pp\n$/s) { 155 if ($inliteral) { 156 $man .= "\0\n"; 157 } elsif ($inlist) { 158 $man .= ".El\n"; 159 $inlist = 0; 160 } else { 161 $man .= ".Pp\n"; 162 } 163 } 164 next; 165 } 166 if (m/^>(\w+)(?:\s+(\d))?$/) { 167 ++$xref{$2 ? "$1 $2" : "$1 3"}; 168 next; 169 } 170 if (s/^\s+(=?\w+):\s*/.It $1/) { 171 if ($inliteral) { 172 $man .= ".Ed\n"; 173 $inliteral = 0; 174 } 175 if (!$inlist) { 176 $man =~ s/\.Pp\n$//s; 177 $man .= ".Bl -tag -width 18n\n"; 178 $inlist = 1; 179 } 180 s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs; 181 $man .= "$_\n"; 182 next; 183 } elsif ($inlist && m/^\S/) { 184 $man .= ".El\n"; 185 $inlist = 0; 186 } elsif ($inliteral && m/^\S/) { 187 $man .= ".Ed\n"; 188 $inlist = 0; 189 } elsif ($inliteral) { 190 $man .= "$_\n"; 191 next; 192 } elsif ($inlist) { 193 s/^\s+//; 194 } elsif (m/^\s+/) { 195 $man .= ".Bd -literal\n"; 196 $inliteral = 1; 197 $man .= "$_\n"; 198 next; 199 } 200 s/\s*=$func\b\s*/\n.Nm\n/gs; 201 s/\s*=$argnames\b\s*/\n.Va $1\n/gs; 202 s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs; 203 if (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/gs) { 204 ++$xref{"$1 3"}; 205 } 206 s/\s*\"(?=\w)/\n.Do\n/gs; 207 s/\"(?!\w)\s*/\n.Dc\n/gs; 208 s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs; 209 s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs; 210 s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs; 211 $man .= "$_\n"; 212 } 213 if (defined($man)) { 214 $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs; 215 $man =~ s/\s*$/\n/gm; 216 $man =~ s/\n+/\n/gs; 217 $man =~ s/\0//gs; 218 chomp($man); 219 } else { 220 $man = "No description available."; 221 } 222 223 $FUNCTIONS{$func} = { 224 'name' => $func, 225 'descr' => $descr, 226 'type' => $type, 227 'args' => $args, 228 'man' => $man, 229 'xref' => \%xref, 230 'errors' => \@errors, 231 }; 232 if ($source =~ m/^ \* NODOC\s*$/m) { 233 $FUNCTIONS{$func}->{'nodoc'} = 1; 234 $FUNCTIONS{$func}->{'nolist'} = 1; 235 } 236 if ($source =~ m/^ \* NOLIST\s*$/m) { 237 $FUNCTIONS{$func}->{'nolist'} = 1; 238 } 239 if ($source !~ m/^ \* XSSO \d/m) { 240 $FUNCTIONS{$func}->{'openpam'} = 1; 241 } 242} 243 244sub expand_errors($); 245sub expand_errors($) { 246 my $func = shift; # Ref to function hash 247 248 my %errors; 249 250 if (defined($func->{'recursed'})) { 251 warn("$func->{'name'}(): loop in error spec\n"); 252 return qw(); 253 } 254 $func->{'recursed'} = 1; 255 256 foreach (@{$func->{'errors'}}) { 257 if (m/^(PAM_[A-Z_]+)$/) { 258 if (!defined($PAMERR{$1})) { 259 warn("$func->{'name'}(): unrecognized error: $1\n"); 260 next; 261 } 262 $errors{$1} = 1; 263 } elsif (m/^!(PAM_[A-Z_]+)$/) { 264 # treat negations separately 265 } elsif (m/^=([a-z_]+)$/) { 266 if (!defined($FUNCTIONS{$1})) { 267 warn("$func->{'name'}(): reference to unknown $1()\n"); 268 next; 269 } 270 foreach (expand_errors($FUNCTIONS{$1})) { 271 $errors{$_} = 1; 272 } 273 } else { 274 warn("$func->{'name'}(): invalid error specification: $_\n"); 275 } 276 } 277 foreach (@{$func->{'errors'}}) { 278 if (m/^!(PAM_[A-Z_]+)$/) { 279 delete($errors{$1}); 280 } 281 } 282 delete($func->{'recursed'}); 283 return (sort(keys(%errors))); 284} 285 286sub gendoc($) { 287 my $func = shift; # Ref to function hash 288 289 local *FILE; 290 my $mdoc; 291 my $fn; 292 293 return if defined($func->{'nodoc'}); 294 295 $mdoc = "$COPYRIGHT 296.Dd $TODAY 297.Dt " . uc($func->{'name'}) . " 3 298.Os 299.Sh NAME 300.Nm $func->{'name'} 301.Nd $func->{'descr'} 302.Sh LIBRARY 303.Lb libpam 304.Sh SYNOPSIS 305.In security/pam_appl.h 306"; 307 if ($func->{'name'} =~ m/_sm_/) { 308 $mdoc .= ".In security/pam_modules.h\n" 309 } 310 $mdoc .= ".Ft $func->{'type'} 311.Fn $func->{'name'} $func->{'args'} 312.Sh DESCRIPTION 313$func->{'man'} 314"; 315 if ($func->{'type'} eq "int") { 316 $mdoc .= ".Sh RETURN VALUES 317The 318.Nm 319function returns one of the following values: 320.Bl -tag -width 18n 321"; 322 my @errors = expand_errors($func); 323 warn("$func->{'name'}(): no error specification\n") 324 unless(@errors); 325 foreach (@errors) { 326 $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n"; 327 } 328 $mdoc .= ".El\n"; 329 } else { 330 if ($func->{'type'} =~ m/\*$/) { 331 $mdoc .= ".Sh RETURN VALUES 332The 333.Nm 334function returns 335.Dv NULL 336on failure. 337"; 338 } 339 } 340 $mdoc .= ".Sh SEE ALSO\n"; 341 my @xref = sort(keys(%{$func->{'xref'}})); 342 while (@xref) { 343 $mdoc .= ".Xr " . shift(@xref) . (@xref ? " ,\n" : "\n"); 344 } 345 $mdoc .= ".Sh STANDARDS\n"; 346 if ($func->{'openpam'}) { 347 $mdoc .= "The 348.Nm 349function is an OpenPAM extension. 350"; 351 } else { 352 $mdoc .= ".Rs 353.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 354.%D \"June 1997\" 355.Re 356"; 357 } 358 $mdoc .= ".Sh AUTHORS 359The 360.Nm 361function and this manual page were developed for the FreeBSD Project 362by ThinkSec AS and NAI Labs, the Security Research Division of Network 363Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 364.Pq Dq CBOSS , 365as part of the DARPA CHATS research program. 366"; 367 368 $fn = "$func->{'name'}.3"; 369 sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC) 370 or die("$fn: open(): $!\n"); 371 print(FILE $mdoc); 372 close(FILE); 373} 374 375sub gensummary() { 376 377 my $func; 378 379 print "$COPYRIGHT 380.Dd $TODAY 381.Dt PAM 3 382.Os 383.Sh NAME 384"; 385 my @funcs = sort(keys(%FUNCTIONS)); 386 while ($func = shift(@funcs)) { 387 next if (defined($FUNCTIONS{$func}->{'nolist'})); 388 print ".Nm $func". (@funcs ? " ,\n" : "\n"); 389 } 390 print ".Nd Pluggable Authentication Modules Library 391.Sh LIBRARY 392.Lb libpam 393.Sh SYNOPSIS 394.In security/pam_appl.h 395"; 396 foreach $func (sort(keys(%FUNCTIONS))) { 397 next if (defined($FUNCTIONS{$func}->{'nolist'})); 398 print ".Ft $FUNCTIONS{$func}->{'type'}\n"; 399 print ".Fn $func $FUNCTIONS{$func}->{'args'}\n"; 400 } 401 print ".Sh DESCRIPTION 402.Sh RETURN VALUES 403The following return codes are defined in the 404.In security/pam_constants.h 405header: 406.Bl -tag -width 18n 407"; 408 foreach (sort(keys(%PAMERR))) { 409 print ".It Bq Er $_\n$PAMERR{$_}.\n"; 410 } 411 print ".El 412.Sh SEE ALSO 413"; 414 foreach $func (sort(keys(%FUNCTIONS))) { 415 next if (defined($FUNCTIONS{$func}->{'nolist'})); 416 print ".Xr $func 3 ,\n"; 417 } 418 print ".Xr pam.conf 5 419.Sh STANDARDS 420.Rs 421.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 422.%D \"June 1997\" 423.Re 424.Sh AUTHORS 425The OpenPAM library and this manual page were developed for the 426FreeBSD Project by ThinkSec AS and NAI Labs, the Security Research 427Division of Network Associates, Inc. under DARPA/SPAWAR contract 428N66001-01-C-8035 429.Pq Dq CBOSS , 430as part of the DARPA CHATS research program. 431" 432} 433 434MAIN:{ 435 $TODAY = strftime("%B %e, %Y", localtime(time())); 436 $TODAY =~ s,\s+, ,g; 437 foreach my $fn (@ARGV) { 438 parse_source($fn); 439 } 440 foreach my $func (values(%FUNCTIONS)) { 441 gendoc($func); 442 } 443 gensummary(); 444} 445