3 # Copyright (c) 2002-2003 Networks Associates Technology, Inc.
4 # Copyright (c) 2004-2011 Dag-Erling Smørgrav
7 # This software was developed for the FreeBSD Project by ThinkSec AS and
8 # Network Associates Laboratories, the Security Research Division of
9 # Network Associates, Inc. under DARPA/SPAWAR contract N66001-01-C-8035
10 # ("CBOSS"), as part of the DARPA CHATS research program.
12 # Redistribution and use in source and binary forms, with or without
13 # modification, are permitted provided that the following conditions
15 # 1. Redistributions of source code must retain the above copyright
16 # notice, this list of conditions and the following disclaimer.
17 # 2. Redistributions in binary form must reproduce the above copyright
18 # notice, this list of conditions and the following disclaimer in the
19 # documentation and/or other materials provided with the distribution.
20 # 3. The name of the author may not be used to endorse or promote
21 # products derived from this software without specific prior written
24 # THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
25 # ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
26 # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27 # ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
28 # FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29 # DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30 # OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31 # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32 # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33 # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
36 # $Id: gendoc.pl 736 2013-09-07 12:52:42Z des $
45 use POSIX qw(strftime);
46 use vars qw(%AUTHORS $TODAY %FUNCTIONS %PAMERR);
49 THINKSEC => "developed for the
51 Project by ThinkSec AS and Network Associates Laboratories, the
52 Security Research Division of Network Associates, Inc.\\& under
53 DARPA/SPAWAR contract N66001-01-C-8035
55 as part of the DARPA CHATS research program.
57 The OpenPAM library is maintained by
58 .An Dag-Erling Sm\\(/orgrav Aq des\@des.no .",
59 UIO => "developed for the University of Oslo by
60 .An Dag-Erling Sm\\(/orgrav Aq des\@des.no .",
62 .An Dag-Erling Sm\\(/orgrav Aq des\@des.no .",
66 PAM_SUCCESS => "Success",
67 PAM_OPEN_ERR => "Failed to load module",
68 PAM_SYMBOL_ERR => "Invalid symbol",
69 PAM_SERVICE_ERR => "Error in service module",
70 PAM_SYSTEM_ERR => "System error",
71 PAM_BUF_ERR => "Memory buffer error",
72 PAM_CONV_ERR => "Conversation failure",
73 PAM_PERM_DENIED => "Permission denied",
74 PAM_MAXTRIES => "Maximum number of tries exceeded",
75 PAM_AUTH_ERR => "Authentication error",
76 PAM_NEW_AUTHTOK_REQD => "New authentication token required",
77 PAM_CRED_INSUFFICIENT => "Insufficient credentials",
78 PAM_AUTHINFO_UNAVAIL => "Authentication information is unavailable",
79 PAM_USER_UNKNOWN => "Unknown user",
80 PAM_CRED_UNAVAIL => "Failed to retrieve user credentials",
81 PAM_CRED_EXPIRED => "User credentials have expired",
82 PAM_CRED_ERR => "Failed to set user credentials",
83 PAM_ACCT_EXPIRED => "User account has expired",
84 PAM_AUTHTOK_EXPIRED => "Password has expired",
85 PAM_SESSION_ERR => "Session failure",
86 PAM_AUTHTOK_ERR => "Authentication token failure",
87 PAM_AUTHTOK_RECOVERY_ERR => "Failed to recover old authentication token",
88 PAM_AUTHTOK_LOCK_BUSY => "Authentication token lock busy",
89 PAM_AUTHTOK_DISABLE_AGING => "Authentication token aging disabled",
90 PAM_NO_MODULE_DATA => "Module data not found",
91 PAM_IGNORE => "Ignore this module",
92 PAM_ABORT => "General failure",
93 PAM_TRY_AGAIN => "Try again",
94 PAM_MODULE_UNKNOWN => "Unknown module type",
95 PAM_DOMAIN_UNKNOWN => "Unknown authentication domain",
120 if ($fn !~ m,\.c$,) {
121 warn("$fn: not C source, ignoring\n");
125 open(FILE, "<", "$fn")
126 or die("$fn: open(): $!\n");
127 $source = join('', <FILE>);
131 if ($source =~ m/^ \* NOPARSE\s*$/m);
133 if ($source =~ m/(\$Id:[^\$]+\$)/) {
137 $author = 'THINKSEC';
138 if ($source =~ s/^ \* AUTHOR\s+(\w*)\s*$//m) {
142 if ($source =~ s/^ \* DEPRECATED\s*(\w*)\s*$//m) {
143 $deprecated = $1 // 0;
146 if ($source =~ s/^ \* EXPERIMENTAL\s*$//m) {
151 $func =~ s,^(?:.*/)?([^/]+)\.c$,$1,;
152 if ($source !~ m,\n \* ([\S ]+)\n \*/\n\n([\S ]+)\n$func\((.*?)\)\n\{,s) {
153 warn("$fn: can't find $func\n");
156 ($descr, $type, $args) = ($1, $2, $3);
157 $descr =~ s,^([A-Z][a-z]),lc($1),e;
158 $descr =~ s,[\.\s]*$,,;
159 while ($args =~ s/^((?:[^\(]|\([^\)]*\))*),\s*/$1\" \"/g) {
162 $args =~ s/,\s+/, /gs;
169 if ($type eq "int") {
170 foreach (split("\n", $source)) {
171 next unless (m/^ \*\s+(!?PAM_[A-Z_]+|=[a-z_]+)\s*$/);
174 ++$xref{3}->{pam_strerror};
178 # extract names of regular arguments
179 $argnames =~ s/\"[^\"]+\*?\b(\w+)\"/\"$1\"/g;
180 # extract names of function pointer arguments
181 $argnames =~ s/\"([\w\s\*]+)\(\*?(\w+)\)\([^\)]+\)\"/\"$2\"/g;
182 # escape metacharacters (there shouldn't be any, but...)
183 $argnames =~ s/([\|\[\]\(\)\.\*\+\?])/\\$1/g;
184 # separate argument names with |
185 $argnames =~ s/\" \"/|/g;
186 # and surround with ()
187 $argnames =~ s/^\"(.*)\"$/$1/;
188 # $argnames is now a regexp that matches argument names
189 $inliteral = $inlist = $intaglist = 0;
190 foreach (split("\n", $source)) {
192 if (!defined($man)) {
198 last if (m/^ \*\/$/);
202 # paragraph separator
203 if ($inlist || $intaglist) {
204 # either a blank line between list items, or a blank
205 # line after the final list item. The latter case
206 # will be handled further down.
209 if ($man =~ m/\n\.Sh [^\n]+\n$/s) {
210 # a blank line after a section header
213 if ($man ne "" && $man !~ m/\.Pp\n$/s) {
222 if (m/^>(\w+)(\s+\d)?$/) {
223 # "see also" cross-reference
224 my ($page, $sect) = ($1, $2 ? int($2) : 3);
225 ++$xref{$sect}->{$page};
228 if (s/^([A-Z][0-9A-Z -]+)$/.Sh $1/) {
229 if ($1 eq "RETURN VALUES") {
232 $man =~ s/\n\.Pp$/\n/s;
237 # item in bullet list
243 $man .= ".El\n.Pp\n";
247 $man =~ s/\.Pp\n$//s;
248 $man .= ".Bl -bullet\n";
253 } elsif (s/^\s+(\S+):\s*/.It $1/) {
260 $man .= ".El\n.Pp\n";
264 $man =~ s/\.Pp\n$//s;
265 $man .= ".Bl -tag -width 18n\n";
268 s/^\.It [=;]([A-Za-z][0-9A-Za-z_]+)$/.It Dv $1/gs;
271 } elsif (($inlist || $intaglist) && m/^\S/) {
272 # regular text after list
273 $man .= ".El\n.Pp\n";
274 $inlist = $intaglist = 0;
275 } elsif ($inliteral && m/^\S/) {
276 # regular text after literal section
279 } elsif ($inliteral) {
280 # additional text within literal section
283 } elsif ($inlist || $intaglist) {
284 # additional text within list
287 # new literal section
288 $man .= ".Bd -literal\n";
293 s/\s*=($func)\b\s*/\n.Fn $1\n/gs;
294 s/\s*=($argnames)\b\s*/\n.Fa $1\n/gs;
295 s/\s*=(struct \w+(?: \*)?)\b\s*/\n.Vt $1\n/gs;
296 s/\s*:([a-z][0-9a-z_]+)\b\s*/\n.Va $1\n/gs;
297 s/\s*;([a-z][0-9a-z_]+)\b\s*/\n.Dv $1\n/gs;
298 s/\s*=!([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/gs;
299 while (s/\s*=([a-z][0-9a-z_]+)\b\s*/\n.Xr $1 3\n/s) {
302 s/\s*\"(?=\w)/\n.Do\n/gs;
303 s/\"(?!\w)\s*/\n.Dc\n/gs;
304 s/\s*=([A-Z][0-9A-Z_]+)\b\s*(?![\.,:;])/\n.Dv $1\n/gs;
305 s/\s*=([A-Z][0-9A-Z_]+)\b([\.,:;]+)\s*/\n.Dv $1 $2\n/gs;
306 s/\s*{([A-Z][a-z] .*?)}\s*/\n.$1\n/gs;
310 if ($inlist || $intaglist) {
312 $inlist = $intaglist = 0;
318 $man =~ s/\%/\\&\%/gs;
319 $man =~ s/(\n\.[A-Z][a-z] [\w ]+)\n([.,:;-])\s+/$1 $2\n/gs;
320 $man =~ s/\s*$/\n/gm;
323 $man =~ s/\n\n\./\n\./gs;
326 $man = "No description available.";
329 $FUNCTIONS{$func} = {
331 'version' => $version,
338 'errors' => \@errors,
340 'customrv' => $customrv,
341 'deprecated' => $deprecated,
342 'experimental' => $experimental,
344 if ($source =~ m/^ \* NODOC\s*$/m) {
345 $FUNCTIONS{$func}->{nodoc} = 1;
347 if ($source !~ m/^ \* XSSO \d/m) {
348 $FUNCTIONS{$func}->{openpam} = 1;
350 expand_errors($FUNCTIONS{$func});
351 return $FUNCTIONS{$func};
354 sub expand_errors($);
355 sub expand_errors($) {
356 my $func = shift; # Ref to function hash
362 if (defined($$func{recursed})) {
363 warn("$$func{name}(): loop in error spec\n");
366 $$func{recursed} = 1;
368 foreach (@{$$func{errors}}) {
369 if (m/^(PAM_[A-Z_]+)$/) {
370 if (!defined($PAMERR{$1})) {
371 warn("$$func{name}(): unrecognized error: $1\n");
375 } elsif (m/^!(PAM_[A-Z_]+)$/) {
376 # treat negations separately
377 } elsif (m/^=([a-z_]+)$/) {
379 if (!defined($FUNCTIONS{$ref})) {
380 $fn = $$func{source};
381 $fn =~ s/$$func{name}/$ref/;
384 if (!defined($FUNCTIONS{$ref})) {
385 warn("$$func{name}(): reference to unknown $ref()\n");
388 foreach (@{$FUNCTIONS{$ref}->{errors}}) {
392 warn("$$func{name}(): invalid error specification: $_\n");
395 foreach (@{$$func{errors}}) {
396 if (m/^!(PAM_[A-Z_]+)$/) {
400 delete($$func{recursed});
401 $$func{errors} = [ sort(keys(%errors)) ];
404 sub dictionary_order($$) {
407 $a =~ s/[^[:alpha:]]//g;
408 $b =~ s/[^[:alpha:]]//g;
413 my $xref = shift; # References
417 foreach my $sect (sort(keys(%{$xref}))) {
418 foreach my $page (sort(dictionary_order keys(%{$xref->{$sect}}))) {
419 push(@refs, "$page $sect");
422 while ($_ = shift(@refs)) {
424 (@refs ? " ,\n" : "\n");
430 my $func = shift; # Ref to function hash
436 return if defined($$func{nodoc});
438 $$func{source} =~ m/([^\/]+)$/;
439 $mdoc = ".\\\" Generated from $1 by gendoc.pl\n";
440 if ($$func{version}) {
441 $mdoc .= ".\\\" $$func{version}\n";
444 .Dt " . uc($$func{name}) . " 3
454 if ($$func{args} =~ m/\bFILE \*\b/) {
455 $mdoc .= ".In stdio.h\n";
457 $mdoc .= ".In security/pam_appl.h
459 if ($$func{name} =~ m/_sm_/) {
460 $mdoc .= ".In security/pam_modules.h\n";
462 if ($$func{name} =~ m/openpam/) {
463 $mdoc .= ".In security/openpam.h\n";
465 $mdoc .= ".Ft \"$$func{type}\"
466 .Fn $$func{name} $$func{args}
469 if (defined($$func{deprecated})) {
470 $mdoc .= ".Bf Sy\n" .
471 "This function is deprecated and may be removed " .
472 "in a future release without further warning.\n";
473 if ($$func{deprecated}) {
474 $mdoc .= "The\n.Fn $$func{deprecated}\nfunction " .
475 "may be used to achieve similar results.\n";
477 $mdoc .= ".Ef\n.Pp\n";
479 if ($$func{experimental}) {
480 $mdoc .= ".Bf Sy\n" .
481 "This function is experimental and may be modified or removed " .
482 "in a future release without prior warning.\n";
483 $mdoc .= ".Ef\n.Pp\n";
485 $mdoc .= "$$func{man}\n";
486 my @errors = @{$$func{errors}};
487 if ($$func{customrv}) {
489 } elsif ($$func{type} eq "int" && @errors) {
490 $mdoc .= ".Sh RETURN VALUES
493 function returns one of the following values:
497 $mdoc .= ".It Bq Er $_\n$PAMERR{$_}.\n";
500 } elsif ($$func{type} eq "int") {
501 $mdoc .= ".Sh RETURN VALUES
504 function returns 0 on success and -1 on failure.
506 } elsif ($$func{type} =~ m/\*$/) {
507 $mdoc .= ".Sh RETURN VALUES
514 } elsif ($$func{type} ne "void") {
515 warn("$$func{name}(): no error specification\n");
517 $mdoc .= ".Sh SEE ALSO\n" . genxref($$func{xref});
518 $mdoc .= ".Sh STANDARDS\n";
519 if ($$func{openpam}) {
522 function is an OpenPAM extension.
526 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
531 $mdoc .= ".Sh AUTHORS
534 function and this manual page were\n";
535 $mdoc .= $AUTHORS{$$func{author} // 'THINKSEC_DARPA'} . "\n";
536 $fn = "$$func{name}.3";
537 if (open(FILE, ">", $fn)) {
541 warn("$fn: open(): $!\n");
546 my $fn = shift; # File name
551 open(FILE, "<", "$fn")
552 or die("$fn: open(): $!\n");
554 if (m/^\.Nm ((?:open)?pam_.*?)\s*$/) {
555 $func{Nm} = $func{Nm} || $1;
556 } elsif (m/^\.Ft (\S.*?)\s*$/) {
557 $func{Ft} = $func{Ft} || $1;
558 } elsif (m/^\.Fn (\S.*?)\s*$/) {
559 $func{Fn} = $func{Fn} || $1;
564 $FUNCTIONS{$func{Nm}} = \%func;
566 warn("No function found\n");
571 my $page = shift; # Which page to produce
578 open(FILE, ">", "$page.3")
579 or die("$page.3: $!\n");
583 print FILE ".\\\" Generated by gendoc.pl
589 my @funcs = sort(keys(%FUNCTIONS));
590 while ($func = shift(@funcs)) {
591 print FILE ".Nm $FUNCTIONS{$func}->{Nm}";
596 print FILE ".Nd Pluggable Authentication Modules Library
600 if ($page eq 'pam') {
601 print FILE ".In security/pam_appl.h\n";
603 print FILE ".In security/openpam.h\n";
605 foreach $func (sort(keys(%FUNCTIONS))) {
606 print FILE ".Ft $FUNCTIONS{$func}->{Ft}\n";
607 print FILE ".Fn $FUNCTIONS{$func}->{Fn}\n";
610 if (m/^\.Xr (\S+)\s*(\d)\s*$/) {
611 ++$xref{int($2)}->{$1};
616 if ($page eq 'pam') {
617 print FILE ".Sh RETURN VALUES
618 The following return codes are defined by
619 .In security/pam_constants.h :
622 foreach (sort(keys(%PAMERR))) {
623 print FILE ".It Bq Er $_\n$PAMERR{$_}.\n";
627 print FILE ".Sh SEE ALSO
629 if ($page eq 'pam') {
630 ++$xref{3}->{openpam};
632 foreach $func (keys(%FUNCTIONS)) {
635 print FILE genxref(\%xref);
636 print FILE ".Sh STANDARDS
638 .%T \"X/Open Single Sign-On Service (XSSO) - Pluggable Authentication Modules\"
642 The OpenPAM library and this manual page were developed for the
644 Project by ThinkSec AS and Network Associates Laboratories, the
645 Security Research Division of Network Associates, Inc.\\& under
646 DARPA/SPAWAR contract N66001-01-C-8035
648 as part of the DARPA CHATS research program.
650 The OpenPAM library is maintained by
651 .An Dag-Erling Sm\\(/orgrav Aq des\@des.no .
658 print(STDERR "usage: gendoc [-op] source [...]\n");
666 unless (@ARGV && getopts("op", \%opts));
667 $TODAY = strftime("%B %e, %Y", localtime(time()));
669 if ($opts{o} || $opts{p}) {
670 foreach my $fn (@ARGV) {
673 gensummary('openpam')
678 foreach my $fn (@ARGV) {
679 my $func = parse_source($fn);