xref: /freebsd/contrib/openpam/misc/gendoc.pl (revision c17d43407fe04133a94055b0dbc7ea8965654a9f)
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