1#!/usr/bin/perl -w 2#- 3# Copyright (c) 2002-2003 Networks Associates Technology, Inc. 4# All rights reserved. 5# 6# This software was developed for the FreeBSD Project by ThinkSec AS and 7# Network Associates Laboratories, the Security Research Division of 8# Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 9# ("CBOSS"), as part of the 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#30 $ 36# 37 38use strict; 39use locale; 40use Fcntl; 41use Getopt::Std; 42use POSIX qw(locale_h strftime); 43use vars qw($COPYRIGHT $TODAY %FUNCTIONS %PAMERR); 44 45$COPYRIGHT = ".\\\"- 46.\\\" Copyright (c) 2001-2003 Networks Associates Technology, Inc. 47.\\\" All rights reserved. 48.\\\" 49.\\\" This software was developed for the FreeBSD Project by ThinkSec AS and 50.\\\" Network Associates Laboratories, the Security Research Division of 51.\\\" Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035 52.\\\" (\"CBOSS\"), as part of the DARPA CHATS research program. 53.\\\" 54.\\\" Redistribution and use in source and binary forms, with or without 55.\\\" modification, are permitted provided that the following conditions 56.\\\" are met: 57.\\\" 1. Redistributions of source code must retain the above copyright 58.\\\" notice, this list of conditions and the following disclaimer. 59.\\\" 2. Redistributions in binary form must reproduce the above copyright 60.\\\" notice, this list of conditions and the following disclaimer in the 61.\\\" documentation and/or other materials provided with the distribution. 62.\\\" 3. The name of the author may not be used to endorse or promote 63.\\\" products derived from this software without specific prior written 64.\\\" permission. 65.\\\" 66.\\\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 67.\\\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 68.\\\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 69.\\\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 70.\\\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 71.\\\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 72.\\\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 73.\\\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 74.\\\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 75.\\\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 76.\\\" SUCH DAMAGE. 77.\\\" 78.\\\" \$" . "P4" . "\$ 79.\\\""; 80 81%PAMERR = ( 82 PAM_SUCCESS => "Success", 83 PAM_OPEN_ERR => "Failed to load module", 84 PAM_SYMBOL_ERR => "Invalid symbol", 85 PAM_SERVICE_ERR => "Error in service module", 86 PAM_SYSTEM_ERR => "System error", 87 PAM_BUF_ERR => "Memory buffer error", 88 PAM_CONV_ERR => "Conversation failure", 89 PAM_PERM_DENIED => "Permission denied", 90 PAM_MAXTRIES => "Maximum number of tries exceeded", 91 PAM_AUTH_ERR => "Authentication error", 92 PAM_NEW_AUTHTOK_REQD => "New authentication token required", 93 PAM_CRED_INSUFFICIENT => "Insufficient credentials", 94 PAM_AUTHINFO_UNAVAIL => "Authentication information is unavailable", 95 PAM_USER_UNKNOWN => "Unknown user", 96 PAM_CRED_UNAVAIL => "Failed to retrieve user credentials", 97 PAM_CRED_EXPIRED => "User credentials have expired", 98 PAM_CRED_ERR => "Failed to set user credentials", 99 PAM_ACCT_EXPIRED => "User account has expired", 100 PAM_AUTHTOK_EXPIRED => "Password has expired", 101 PAM_SESSION_ERR => "Session failure", 102 PAM_AUTHTOK_ERR => "Authentication token failure", 103 PAM_AUTHTOK_RECOVERY_ERR => "Failed to recover old authentication token", 104 PAM_AUTHTOK_LOCK_BUSY => "Authentication token lock busy", 105 PAM_AUTHTOK_DISABLE_AGING => "Authentication token aging disabled", 106 PAM_NO_MODULE_DATA => "Module data not found", 107 PAM_IGNORE => "Ignore this module", 108 PAM_ABORT => "General failure", 109 PAM_TRY_AGAIN => "Try again", 110 PAM_MODULE_UNKNOWN => "Unknown module type", 111 PAM_DOMAIN_UNKNOWN => "Unknown authentication domain", 112); 113 114sub parse_source($) { 115 my $fn = shift; 116 117 local *FILE; 118 my $source; 119 my $func; 120 my $descr; 121 my $type; 122 my $args; 123 my $argnames; 124 my $man; 125 my $inlist; 126 my $inliteral; 127 my %xref; 128 my @errors; 129 130 if ($fn !~ m,\.c$,) { 131 warn("$fn: not C source, ignoring\n"); 132 return undef; 133 } 134 135 sysopen(FILE, $fn, O_RDONLY) 136 or die("$fn: open(): $!\n"); 137 $source = join('', <FILE>); 138 close(FILE); 139 140 return undef 141 if ($source =~ m/^ \* NOPARSE\s*$/m); 142 143 $func = $fn; 144 $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,; 145 if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) { 146 warn("$fn: can't find $func\n"); 147 return undef; 148 } 149 ($descr, $type, $args) = ($1, $2, $3); 150 $descr =~ s,^([A-Z][a-z]),lc($1),e; 151 $descr =~ s,[\.\s]*$,,; 152 while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) { 153 # nothing 154 } 155 $args =~ s/,\s+/, /gs; 156 $args = "\"$args\""; 157 158 %xref = ( 159 3 => { 'pam' => 1 }, 160 ); 161 162 if ($type eq "int") { 163 foreach (split("\n", $source)) { 164 next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/); 165 push(@errors, $1); 166 } 167 ++$xref{3}->{'pam_strerror'}; 168 } 169 170 $argnames = $args; 171 # extract names of regular arguments 172 $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g; 173 # extract names of function pointer arguments 174 $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g; 175 # escape metacharacters (there shouldn't be any, but...) 176 $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g; 177 # separate argument names with | 178 $argnames =~ s/\" \"/|/g; 179 # and surround with () 180 $argnames =~ s/^\"(.*)\"$/($1)/; 181 # $argnames is now a regexp that matches argument names 182 $inliteral = $inlist = 0; 183 foreach (split("\n", $source)) { 184 s/\s*$//; 185 if (!defined($man)) { 186 if (m/^\/\*\*$/) { 187 $man = ""; 188 } 189 next; 190 } 191 last if (m/^ \*\/$/); 192 s/^ \* ?//; 193 s/\\(.)/$1/gs; 194 if (m/^$/) { 195 if ($man ne "" && $man !~ m/\.Pp\n$/s) { 196 if ($inliteral) { 197 $man .= "\0\n"; 198 } elsif ($inlist) { 199 $man .= ".El\n.Pp\n"; 200 $inlist = 0; 201 } else { 202 $man .= ".Pp\n"; 203 } 204 } 205 next; 206 } 207 if (m/^>(\w+)(\s+\d)?$/) { 208 my ($page, $sect) = ($1, $2 ? int($2) : 3); 209 ++$xref{$sect}->{$page}; 210 next; 211 } 212 if (s/^\s+(=?\w+):\s*/.It $1/) { 213 if ($inliteral) { 214 $man .= ".Ed\n"; 215 $inliteral = 0; 216 } 217 if (!$inlist) { 218 $man =~ s/\.Pp\n$//s; 219 $man .= ".Bl -tag -width 18n\n"; 220 $inlist = 1; 221 } 222 s/^\.It =([A-Z][A-Z_]+)$/.It Dv $1/gs; 223 $man .= "$_\n"; 224 next; 225 } elsif ($inlist && m/^\S/) { 226 $man .= ".El\n.Pp\n"; 227 $inlist = 0; 228 } elsif ($inliteral && m/^\S/) { 229 $man .= ".Ed\n"; 230 $inliteral = 0; 231 } elsif ($inliteral) { 232 $man .= "$_\n"; 233 next; 234 } elsif ($inlist) { 235 s/^\s+//; 236 } elsif (m/^\s+/) { 237 $man .= ".Bd -literal\n"; 238 $inliteral = 1; 239 $man .= "$_\n"; 240 next; 241 } 242 s/\s*=$func\b\s*/\n.Nm\n/gs; 243 s/\s*=$argnames\b\s*/\n.Fa $1\n/gs; 244 s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs; 245 s/\s*:([a-z_]+)\b\s*/\n.Va $1\n/gs; 246 s/\s*;([a-z_]+)\b\s*/\n.Dv $1\n/gs; 247 while (s/\s*=([a-z_]+)\b\s*/\n.Xr $1 3\n/s) { 248 ++$xref{3}->{$1}; 249 } 250 s/\s*\"(?=\w)/\n.Do\n/gs; 251 s/\"(?!\w)\s*/\n.Dc\n/gs; 252 s/\s*=([A-Z][A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs; 253 s/\s*=([A-Z][A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs; 254 s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs; 255 $man .= "$_\n"; 256 } 257 if (defined($man)) { 258 if ($inlist) { 259 $man .= ".El\n"; 260 } 261 if ($inliteral) { 262 $man .= ".Ed\n"; 263 } 264 $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([\.,:;-]\S*)\s*/$1 $2\n/gs; 265 $man =~ s/\s*$/\n/gm; 266 $man =~ s/\n+/\n/gs; 267 $man =~ s/\0//gs; 268 $man =~ s/\n\n\./\n\./gs; 269 chomp($man); 270 } else { 271 $man = "No description available."; 272 } 273 274 $FUNCTIONS{$func} = { 275 'source' => $fn, 276 'name' => $func, 277 'descr' => $descr, 278 'type' => $type, 279 'args' => $args, 280 'man' => $man, 281 'xref' => \%xref, 282 'errors' => \@errors, 283 }; 284 if ($source =~ m/^ \* NODOC\s*$/m) { 285 $FUNCTIONS{$func}->{'nodoc'} = 1; 286 } 287 if ($source !~ m/^ \* XSSO \d/m) { 288 $FUNCTIONS{$func}->{'openpam'} = 1; 289 } 290 expand_errors($FUNCTIONS{$func}); 291 return $FUNCTIONS{$func}; 292} 293 294sub expand_errors($); 295sub expand_errors($) { 296 my $func = shift; # Ref to function hash 297 298 my %errors; 299 my $ref; 300 my $fn; 301 302 if (defined($func->{'recursed'})) { 303 warn("$func->{'name'}(): loop in error spec\n"); 304 return qw(); 305 } 306 $func->{'recursed'} = 1; 307 308 foreach (@{$func->{'errors'}}) { 309 if (m/^(PAM_[A-Z_]+)$/) { 310 if (!defined($PAMERR{$1})) { 311 warn("$func->{'name'}(): unrecognized error: $1\n"); 312 next; 313 } 314 $errors{$1} = 1; 315 } elsif (m/^!(PAM_[A-Z_]+)$/) { 316 # treat negations separately 317 } elsif (m/^=([a-z_]+)$/) { 318 $ref = $1; 319 if (!defined($FUNCTIONS{$ref})) { 320 $fn = $func->{'source'}; 321 $fn =~ s/$func->{'name'}/$ref/; 322 parse_source($fn); 323 } 324 if (!defined($FUNCTIONS{$ref})) { 325 warn("$func->{'name'}(): reference to unknown $ref()\n"); 326 next; 327 } 328 foreach (@{$FUNCTIONS{$ref}->{'errors'}}) { 329 $errors{$_} = 1; 330 } 331 } else { 332 warn("$func->{'name'}(): invalid error specification: $_\n"); 333 } 334 } 335 foreach (@{$func->{'errors'}}) { 336 if (m/^!(PAM_[A-Z_]+)$/) { 337 delete($errors{$1}); 338 } 339 } 340 delete($func->{'recursed'}); 341 $func->{'errors'} = [ sort(keys(%errors)) ]; 342} 343 344sub dictionary_order($$) { 345 my ($a, $b) = @_; 346 347 $a =~ s/[^[:alpha:]]//g; 348 $b =~ s/[^[:alpha:]]//g; 349 $a cmp $b; 350} 351 352sub genxref($) { 353 my $xref = shift; # References 354 355 my $mdoc = ''; 356 my @refs = (); 357 foreach my $sect (sort(keys(%{$xref}))) { 358 foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) { 359 push(@refs, "$page $sect"); 360 } 361 } 362 while ($_ = shift(@refs)) { 363 $mdoc .= ".Xr $_" . 364 (@refs ? " ,\n" : "\n"); 365 } 366 return $mdoc; 367} 368 369sub gendoc($) { 370 my $func = shift; # Ref to function hash 371 372 local *FILE; 373 my $mdoc; 374 my $fn; 375 376 return if defined($func->{'nodoc'}); 377 378 $mdoc = "$COPYRIGHT 379.Dd $TODAY 380.Dt " . uc($func->{'name'}) . " 3 381.Os 382.Sh NAME 383.Nm $func->{'name'} 384.Nd $func->{'descr'} 385.Sh LIBRARY 386.Lb libpam 387.Sh SYNOPSIS 388.In sys/types.h 389.In security/pam_appl.h 390"; 391 if ($func->{'name'} =~ m/_sm_/) { 392 $mdoc .= ".In security/pam_modules.h\n" 393 } 394 if ($func->{'name'} =~ m/openpam/) { 395 $mdoc .= ".In security/openpam.h\n" 396 } 397 $mdoc .= ".Ft \"$func->{'type'}\" 398.Fn $func->{'name'} $func->{'args'} 399.Sh DESCRIPTION 400$func->{'man'} 401"; 402 if ($func->{'type'} eq "int") { 403 $mdoc .= ".Sh RETURN VALUES 404The 405.Nm 406function returns one of the following values: 407.Bl -tag -width 18n 408"; 409 my @errors = @{$func->{'errors'}}; 410 warn("$func->{'name'}(): no error specification\n") 411 unless(@errors); 412 foreach (@errors) { 413 $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n"; 414 } 415 $mdoc .= ".El\n"; 416 } else { 417 if ($func->{'type'} =~ m/\*$/) { 418 $mdoc .= ".Sh RETURN VALUES 419The 420.Nm 421function returns 422.Dv NULL 423on failure. 424"; 425 } 426 } 427 $mdoc .= ".Sh SEE ALSO\n" . genxref($func->{'xref'}); 428 $mdoc .= ".Sh STANDARDS\n"; 429 if ($func->{'openpam'}) { 430 $mdoc .= "The 431.Nm 432function is an OpenPAM extension. 433"; 434 } else { 435 $mdoc .= ".Rs 436.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 437.%D \"June 1997\" 438.Re 439"; 440 } 441 $mdoc .= ".Sh AUTHORS 442The 443.Nm 444function and this manual page were developed for the 445.Fx 446Project by ThinkSec AS and Network Associates Laboratories, the 447Security Research Division of Network Associates, Inc.\\& under 448DARPA/SPAWAR contract N66001-01-C-8035 449.Pq Dq CBOSS , 450as part of the DARPA CHATS research program. 451"; 452 453 $fn = "$func->{'name'}.3"; 454 if (sysopen(FILE, $fn, O_RDWR|O_CREAT|O_TRUNC)) { 455 print(FILE $mdoc); 456 close(FILE); 457 } else { 458 warn("$fn: open(): $!\n"); 459 } 460} 461 462sub readproto($) { 463 my $fn = shift; # File name 464 465 local *FILE; 466 my %func; 467 468 sysopen(FILE, $fn, O_RDONLY) 469 or die("$fn: open(): $!\n"); 470 while (<FILE>) { 471 if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) { 472 $func{'Nm'} = $func{'Nm'} || $1; 473 } elsif (m/^\.Ft (\S.*?)\s*$/) { 474 $func{'Ft'} = $func{'Ft'} || $1; 475 } elsif (m/^\.Fn (\S.*?)\s*$/) { 476 $func{'Fn'} = $func{'Fn'} || $1; 477 } 478 } 479 close(FILE); 480 if ($func{'Nm'}) { 481 $FUNCTIONS{$func{'Nm'}} = \%func; 482 } else { 483 warn("No function found\n"); 484 } 485} 486 487sub gensummary($) { 488 my $page = shift; # Which page to produce 489 490 local *FILE; 491 my $upage; 492 my $func; 493 my %xref; 494 495 sysopen(FILE, "$page.3", O_RDWR|O_CREAT|O_TRUNC) 496 or die("$page.3: $!\n"); 497 498 $upage = uc($page); 499 print FILE "$COPYRIGHT 500.Dd $TODAY 501.Dt $upage 3 502.Os 503.Sh NAME 504"; 505 my @funcs = sort(keys(%FUNCTIONS)); 506 while ($func = shift(@funcs)) { 507 print FILE ".Nm $FUNCTIONS{$func}->{'Nm'}"; 508 print FILE " ," 509 if (@funcs); 510 print FILE "\n"; 511 } 512 print FILE ".Nd Pluggable Authentication Modules Library 513.Sh LIBRARY 514.Lb libpam 515.Sh SYNOPSIS\n"; 516 if ($page eq 'pam') { 517 print FILE ".In security/pam_appl.h\n"; 518 } else { 519 print FILE ".In security/openpam.h\n"; 520 } 521 foreach $func (sort(keys(%FUNCTIONS))) { 522 print FILE ".Ft $FUNCTIONS{$func}->{'Ft'}\n"; 523 print FILE ".Fn $FUNCTIONS{$func}->{'Fn'}\n"; 524 } 525 while (<STDIN>) { 526 if (m/^\.Xr (\S+)\s*(\d)\s*$/) { 527 ++$xref{int($2)}->{$1}; 528 } 529 print FILE $_; 530 } 531 532 if ($page eq 'pam') { 533 print FILE ".Sh RETURN VALUES 534The following return codes are defined by 535.In security/pam_constants.h : 536.Bl -tag -width 18n 537"; 538 foreach (sort(keys(%PAMERR))) { 539 print FILE ".It Bq Er $_\n$PAMERR{$_}.\n"; 540 } 541 print FILE ".El\n"; 542 } 543 print FILE ".Sh SEE ALSO 544"; 545 if ($page eq 'pam') { 546 ++$xref{3}->{'openpam'}; 547 } 548 foreach $func (keys(%FUNCTIONS)) { 549 ++$xref{3}->{$func}; 550 } 551 print FILE genxref(\%xref); 552 print FILE ".Sh STANDARDS 553.Rs 554.%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\" 555.%D \"June 1997\" 556.Re 557.Sh AUTHORS 558The OpenPAM library and this manual page were developed for the 559.Fx 560Project by ThinkSec AS and Network Associates Laboratories, the 561Security Research Division of Network Associates, Inc.\\& under 562DARPA/SPAWAR contract N66001-01-C-8035 563.Pq Dq CBOSS , 564as part of the DARPA CHATS research program. 565"; 566 close(FILE); 567} 568 569sub usage() { 570 571 print(STDERR "usage: gendoc [-s] source [...]\n"); 572 exit(1); 573} 574 575MAIN:{ 576 my %opts; 577 578 usage() 579 unless (@ARGV && getopts("op", \%opts)); 580 setlocale(LC_ALL, "en_US.ISO8859-1"); 581 $TODAY = strftime("%B %e, %Y", localtime(time())); 582 $TODAY =~ s,\s+, ,g; 583 if ($opts{'o'} || $opts{'p'}) { 584 foreach my $fn (@ARGV) { 585 readproto($fn); 586 } 587 gensummary('openpam') 588 if ($opts{'o'}); 589 gensummary('pam') 590 if ($opts{'p'}); 591 } else { 592 foreach my $fn (@ARGV) { 593 my $func = parse_source($fn); 594 gendoc($func) 595 if (defined($func)); 596 } 597 } 598 exit(0); 599} 600