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