Patch for the integration of python hooks and readers.conf
Erik Klavon
erik at eriq.org
Sun Feb 2 18:41:24 UTC 2003
Greetings
Enclosed are materials for the integration of the nnrpd python hooks
and readers.conf. Three new files have been added to samples; all are
wrapper scripts provided to make migration to the new hooks easier. A
diff of changes made to existing files (both code and documentation)
is also included. The diff is against current as of 1000 PST today.
Erik
**************** new files *************************
*** begin samples/nnrpd_access_wrapper.py ***
# Example wrapper nnrpd_access_wrapper.py for support of old python
# authentication scripts, by Erik Klavon.
# This file contains a sample python script which can be used to
# duplicate the behavior of the old nnrppythonauth functionality. This
# script only supports access control.
# How to use this wrapper:
# - insert your authentication class into this file.
# - rename your authentication class OLDAUTH
#
# Old AUTH class
# Insert your old auth class here
# do not include the code which sets the hook
#
# Wrapper AUTH class. It creates an instance of the old class and
# calls its methods. Arguments and return values are munged as
# needed to fit the new way of doing things.
#
class MYAUTH:
"""Provide access callbacks to nnrpd."""
def access_init(self):
self.old = OLDAUTH()
def access(self, attributes):
attributes['type'] = buffer('connect')
perm = (self.old).authenticate(attributes)
result = dict([('users','*')])
if perm[1] == 1:
result['read'] = perm[3]
if perm[2] == 1:
result['post'] = perm[3]
return result
def access_close(self):
(self.old).close()
#
# The rest is used to hook up the auth module on nnrpd. It is unlikely
# you will ever need to modify this.
#
# Import functions exposed by nnrpd. This import must succeed, or nothing
# will work!
from nnrpd import *
# Create a class instance
myauth = MYAUTH()
# ...and try to hook up on nnrpd. This would make auth object methods visible
# to nnrpd.
try:
set_auth_hook(myauth)
syslog('notice', "authentication module successfully hooked into nnrpd")
except Exception, errmsg:
syslog('error', "Cannot obtain nnrpd hook for authentication method: %s" % errmsg[0])
*** end samples/nnrpd_access_wrapper.py ***
*** begin samples/nnrpd_auth_wrapper.py ***
# Example wrapper nnrpd_auth_wrapper.py for support of old python
# authentication scripts, by Erik Klavon.
# This file contains a sample python script which can be used to
# duplicate the behavior of the old nnrppythonauth functionality. This
# script only supports authentication.
# How to use this wrapper:
# - insert your authentication class into this file.
# - rename your authentication class OLDAUTH
#
# Old AUTH class
# Insert your old auth class here
# do not include the code which sets the hook
#
# Wrapper AUTH class. It creates an instance of the old class and
# calls its methods. Arguments and return values are munged as
# needed to fit the new way of doing things.
#
class MYAUTH:
"""Provide auth callbacks to nnrpd."""
def authen_init(self):
self.old = OLDAUTH()
def authenticate(self, attributes):
attributes['type'] = buffer('authinfo')
perm = (self.old).authenticate(attributes)
err_str = "No error"
if perm[0] == 502:
err_str = "Python authentication error!"
return (perm[0],err_str)
def authen_close(self):
(self.old).close()
#
# The rest is used to hook up the auth module on nnrpd. It is unlikely
# you will ever need to modify this.
#
# Import functions exposed by nnrpd. This import must succeed, or nothing
# will work!
from nnrpd import *
# Create a class instance
myauth = MYAUTH()
# ...and try to hook up on nnrpd. This would make auth object methods visible
# to nnrpd.
try:
set_auth_hook(myauth)
syslog('notice', "authentication module successfully hooked into nnrpd")
except Exception, errmsg:
syslog('error', "Cannot obtain nnrpd hook for authentication method: %s" % errmsg[0])
*** end samples/nnrpd_auth_wrapper.py ***
*** begin samples/nnrpd_dynamic_wrapper.py ***
# Example wrapper nnrpd_dynamic_wrapper.py for support of old python
# authentication scripts, by Erik Klavon.
# This file contains a sample python script which can be used to
# duplicate the behavior of the old nnrppythonauth functionality. This
# script only supports dynamic access control by group.
# How to use this wrapper:
# - insert your authentication class into this file.
# - rename your authentication class OLDAUTH
#
# Old AUTH class
# Insert your old auth class here
# do not include the code which sets the hook
#
# Wrapper AUTH class. It creates an instance of the old class and
# calls its methods. Arguments and return values are munged as
# needed to fit the new way of doing things.
#
class MYAUTH:
"""Provide dynamic access callbacks to nnrpd."""
def dynamic_init(self):
self.old = OLDAUTH()
def dynamic(self, attributes):
return (self.old).authorize(attributes)
def dynamic_close(self):
(self.old).close()
#
# The rest is used to hook up the auth module on nnrpd. It is unlikely
# you will ever need to modify this.
#
# Import functions exposed by nnrpd. This import must succeed, or nothing
# will work!
from nnrpd import *
# Create a class instance
myauth = MYAUTH()
# ...and try to hook up on nnrpd. This would make auth object methods visible
# to nnrpd.
try:
set_auth_hook(myauth)
syslog('notice', "authentication module successfully hooked into nnrpd")
except Exception, errmsg:
syslog('error', "Cannot obtain nnrpd hook for authentication method: %s" % errmsg[0])
*** end samples/nnrpd_auth_wrapper.py ***
**************** end new files ****************
**************** diff *************************
diff -ur inn/doc/pod/hook-python.pod inn_python/doc/pod/hook-python.pod
--- inn/doc/pod/hook-python.pod Mon Dec 2 21:17:17 2002
+++ inn_python/doc/pod/hook-python.pod Sat Feb 1 11:03:22 2003
@@ -260,65 +260,240 @@
else:
moderated = "something else"
-=head1 PYTHON AUTHENTICATION AND AUTHORIZATION SUPPORT FOR NNRPD
+=head1 CHANGES TO PYTHON AUTHENTICATION AND ACCESS CONTROL SUPPORT FOR
+NNRPD
-Python authentication and authorization support in nnrpd along with
-filtering support in innd may be compiled in by passing C<--with-python>
-C<configure>. Python authentication and authorization may be enabled with
-the nnrppythonauth setting in F<inn.conf>.
-
-If nnrppythonauth in F<inn.conf> is set to true, nnrpd will load the
-Python module defined in include/paths.h and located in the directory
-specified by pathfilter in F<inn.conf>. Once that module is loaded, nnrpd
-will authenticate and authorize readers by calling a Python methods rather
-than reading F<readers.conf> and using the normal authentication
-mechanism.
-
-Every time an authenticated reader asks nnrpd to read or post an article,
-Python authorization hooks are invoked before proceeding with the
-requested operation. The authorization functionality makes sense when a
-list of newsgroups in your access statements grows too long to maintain in
-F<readers.conf> or you need to have access control rules applied
-immediately without having to restart all the nnrpd processes. Also,
-Python authorization hooks perform access control on per newsgroup basis
-while F<readers.conf> does the same on per user basis.
+The old authentication and access control functionality has been
+combined with the new readers.conf mechanism by Erik Klavon
+<erik at eriq.org>; bug reports should however go to inn-bugs at isc.org,
+not Erik.
+
+The remainder of this section is an introduction to the new mechanism
+(which uses the python_auth: python_access: and python_dynamic:
+f<readers.conf> parameters) with porting/migration suggestions for
+people familiar with the old mechanism (identifiable by the now
+deprecated nnrpperlauth: parameter in F<inn.conf>).
+
+Other people should skip this section.
+
+The python_auth parameter allows the use of Python to authenticate a
+user. Authentication scripts (like those from the old mechanism) are
+listed in F<readers.conf> using python_auth in the same manner other
+authenticators are using auth:
+
+ python_auth: "auth1.py"
+
+Scripts should be placed as before in the filter directory (see the
+pathfilter setting in F<inn.conf>). The new hook method authen_init
+takes no arguments and its return value is ignored; its purpose is to
+provide a means for authentication specific initialization. The hook
+method authen_close is the more specific analogue to the old close
+method. These method hooks are not required.
+
+The argument dictionary passed to authenticate remains the same,
+except for the removal of the "type" entry which is no longer needed
+in this modification. The return tuple now only contains either two or
+three elements, the first of which is the NNTP response code. The
+second is an error string which is passed to the client if the
+response code indicates that the authentication attempt has
+failed. This allows a specific error message to be generated by the
+Python script in place of the generic message "Authentication
+failed". An optional third return element if present will be used to
+match the connection with the user: parameter in access groups and
+will also be the username logged. If this element is absent, the
+username supplied by the client during authentication will be used as
+was the previous behavior.
+
+The python_access parameter (described below) is new; it allows the
+dynamic generation of an access group of an incoming connection using
+a Python script. If a connection matches an auth group which has a
+python_access parameter, all access groups in readers.conf are
+ignored; instead the procedure described below is used to generate an
+access group. This concept is due to Jeffery M. Vinocur.
+
+In the old implementation, the authorization method allowed for access
+control on a per-group basis. That functionality is preserved in the
+new implementation by the inclusion of the python_dynamic parameter in
+F<readers.conf>. The only change is the corresponding method name of
+dynamic as opposed to authorize; domain and range are the same as
+before. Additionally, the associated optional housekeeping methods
+dynamic_init and dynamic_close may be implemented if needed.
+
+This new implementation should provide all of the previous
+capabilities of the Python hooks, in combination with the flexibility
+of readers.conf and the use of other authentication and resolving
+programs (including the Perl hooks!). To use Python code that predates
+the new mechanism, you would need to modify the code slightly (see
+below for the new specification) and supply a simple readers.conf
+file. If you don't want to modify your code, the sample directory has
+F<nnrpd_auth_wrapper.py>, F<nnrpd_access_wrapper.py> and
+F<nnrpd_dynamic_wrapper.py> which should allow you to use your old
+code without needing to change it.
+
+However, before trying to use your old Python code, you may want to
+consider replacing it entirely with non-Python authentication. (With
+readers.conf and the regular authenticator and resolver programs, much
+of what once required Perl can be done directly.) Even if the
+functionality is not available directly, you may wish to write a new
+authenticator or resolver (which can be done in whatever language you
+prefer).
+
+=head1 PYTHON AUTHENTICATION SUPPORT FOR NNRPD
+
+Python authentication, dynamic access group generation and dynamic
+access control support in nnrpd along with filtering support in innd
+may be compiled in by passing C<--with-python> C<configure>.
+
+Support for authentication via Python is provided in nnrpd by the
+inclusion of a python_auth: parameter in a F<readers.conf> auth
+group. python_auth: works exactly like the auth: parameter in
+F<readers.conf>, except that it calls the script given as argument
+using the Python hook rather then treating it as an external
+program. Multiple, mixed use of python_auth: with other auth:
+statements including perl_auth: is permitted. Each auth: statement
+will be tried in the order they appear in the auth group until either
+one succeeds or all are exhausted.
+
+If the processing of readers.conf requires that a python_auth:
+statement be used for authentication, Python is loaded (if it has yet
+to be) and the file given as argument to the python_auth: parameter is
+loaded as well. If a Python object with a method authen_init is hooked in
+during the loading of that file, then that method is called
+immediately after the file is loaded. If no errors have occurred, the
+method authenticate is called. Depending on the NNTP response code
+returned by authenticate, the authentication hook either succeeds or
+fails, after which the processing of the auth group continues as
+usual. When the connection with the client is closed, the method
+authen_close is called if it exists.
+
+=head1 DYNAMIC GENERATION OF ACCESS GROUPS
+
+A Python script may be used to dynamically generate an access group
+which is then used to determine the access rights of the client. This
+occurs whenever the python_access: parameter is specified in an auth group
+which has successfully matched the client. Only one python_access:
+statement is allowed in an auth group. This parameter should not be
+mixed with a perl_access: statement in the same auth group.
+
+When a python_access: parameter is encountered, Python is loaded (if
+it has yet to be) and the file given as argument is loaded as well. If
+a Python object with a method access_init is hooked in during the
+loading of that file, then that method is called immediately after the
+file is loaded. If no errors have occurred, the method access is
+called. The dictionary returned by access is used to generate an
+access group which is then used to determine the access rights of the
+client. When the connection with the client is closed, the method
+access_close is called if it exists.
+
+While you may include the users: parameter in a dynamically generated
+access group, some care should be taken (unless your pattern is just
+* which is equivalent to leaving the parameter out). The group created
+with the values returned from the Python script is the only one
+considered when nnrpd attempts to find an access group matching the
+connection. If a users: parameter is included and it doesn't match the
+connection, then the client will be denied access since there are no
+other access groups which could match the connection.
+
+=head1 DYNAMIC ACCESS CONTROL
+
+If you need to have access control rules applied immediately without
+having to restart all the nnrpd processes, you may apply access
+control on a per newsgroup basis using the Python dynamic hooks (as
+opposed to F<readers.conf> which does the same on per user
+basis). These hooks are activated through the inclusion of the
+python_dynamic: parameter in a F<readers.conf> auth group. Only one
+python_dynamic: statement is allowed in an auth group.
+
+When a python_dynamic: parameter is encountered, Python is loaded (if
+it has yet to be) and the file given as argument is loaded as well. If
+a Python object with a method dynamic_init is hooked in during the
+loading of that file, then that method is called immediately after the
+file is loaded. Every time a reader asks nnrpd to read or post an
+article, the Python method dynamic is invoked before proceeding with
+the requested operation. Based on the value returned by dynamic, the
+operation is either permitted or denied. When the connection with the
+client is closed, the method access_close is called if it exists.
-However, consider the authorization functionality as an option which is
-reasonable in just a few cases (like those mentioned above).
-
-=head1 WRITING A NNRPD AUTHENTICATION MODULE
+=head1 WRITING A PYTHON NNRPD AUTHENTICATION MODULE
You need to create a F<nnrpd_auth.py> module in INN's filter directory
(see the pathfilter setting in F<inn.conf>) where you should define a
-class holding certain methods.
+class holding certain methods depending on which hook(s) you want to
+make use of.
-The following methods are known to nnrpd. It uses them if present:
+The following methods are known to nnrpd:
=over 4
=item __init__(self)
Not explicitly called by nnrpd, but will run whenever the auth module is
-loaded. This is a good place to initialize constants or establish a
-database connection.
+loaded. Use this method to initialize any general variables or open
+a common database connection. This method may be omitted.
+
+=item authen_init(self)
+
+Initialization function specific to authentication. This method may be
+omitted.
+
+=item authenticate(self, attributes)
-=item close(self)
+Called when a python_auth statement is reached in the processing of
+readers.conf. Connection attributes are passed in the "attributes"
+dictionary. Returns a response code, an error string and an optional
+string to appear in the logs as the username and is used to match the
+connection with an access group.
+
+=item authen_close(self)
This method is invoked on nnrpd termination. You can use it to save state
information or close a database connection.
-=item authenticate(self, attributes)
+=item access_init(self)
+
+Initialization function specific to generation of an access
+group. This method may be omitted.
+
+=item access(self, attributes)
+
+Called when a python_access statement is reached in the processing of
+readers.conf. Connection attributes are passed in the "attributes"
+dictionary. Returns a dictionary of values representing statements to
+be included in an access group.
+
+=item access_close(self)
+
+This method is invoked on nnrpd termination. You can use it to save state
+information or close a database connection.
+
+=item dynamic_init(self)
+
+Initialization function specific to dynamic access control. This
+method may be omitted.
+
+=item dynamic(self, attributes)
+
+Called when a client requests a newsgroup, an atricle or attempts to
+post. Connection attributes are passed in the "attributes"
+dictionary. Returns None to grant access, or a non-empty string (which
+will be reported back to the client) otherwise.
+
+=item dynamic_close(self)
-Called when a reader connects or issues AUTHINFO command. Connection
-attributes are passed in the "attributes" dictionary. The following keys
-are initialized by nnrpd:
+This method is invoked on nnrpd termination. You can use it to save state
+information or close a database connection.
+
+=item attributes dictionary
+
+The keys and associated values of the attributes dictionary are
+described below.
=over 4
=item type
-"connect", "authinfo", "read" or "post" values specify the authentication
-type.
+"read" or "post" values specify the authentication
+type. Only valid for dynamic method.
=item hostname
@@ -342,49 +517,13 @@
=item newsgroup
-name of the newsgroup reader requests read or post access to or None if
-not applicable
+name of the newsgroup reader requests read or post access to. Only
+valid for dynamic method.
=back
All the above values are buffer objects (see the notes above on what
buffer objects are).
-
-This method should return a tuple of four elements:
-
-=over 3
-
-=item 1)
-
-NNTP response code. Should be a valid NNTP response code (see example for
-details).
-
-=item 2)
-
-Whether reading is allowed. Should be a boolean value.
-
-=item 3)
-
-Whether posting is allowed. Should be a boolean value.
-
-=item 4)
-
-Wildmat expression that says what groups to provide access to.
-
-=back
-
-See the explanation of applicable NNTP return codes in F<hook-perl> in the
-INN documentation.
-
-=item authorize(self, attributes)
-
-Called when a reader requests either read or post permission. The
-"attributes" dictionary is passed to group() method (see above for
-details).
-
-This method should return None to grant requested permission to requested
-newsgroup or non-empty string otherwise. The rejection string will be
-shown to reader.
=back
diff -ur inn/doc/pod/inn.conf.pod inn_python/doc/pod/inn.conf.pod
--- inn/doc/pod/inn.conf.pod Mon Dec 16 02:48:17 2002
+++ inn_python/doc/pod/inn.conf.pod Sat Feb 1 11:03:22 2003
@@ -572,10 +572,7 @@
=item I<nnrppythonauth>
-Whether to use the Python hook in nnrpd(8) to authenticate readers. If
-this is enabled, normal readers.conf(5) authentication will not be used,
-and instead the python hook will be called to authenticate connections.
-This is a boolean value and the default is false.
+This parameter is now obsolete; see "Changes to Python Authentication and Access Control Support for nnrpd" in F<doc/hook-python>.
=item I<noreader>
diff -ur inn/doc/pod/readers.conf.pod inn_python/doc/pod/readers.conf.pod
--- inn/doc/pod/readers.conf.pod Sun Jan 19 14:10:57 2003
+++ inn_python/doc/pod/readers.conf.pod Sat Feb 1 11:03:22 2003
@@ -96,13 +96,13 @@
<res-program> only returns a username, <defdomain> is used as the
domain.
-If the user later authenticates via the AUTHINFO USER/PASS commands, the
-provided username and password are passed to each <auth-program>
-(multiple auth: or perl_auth: lines may be present in a block; they are
-run in sequence until one succeeds), if any. If one succeeds and
-returns a different identity than the one assigned at the time of the
-connection, it is matched against the available access groups again and
-the actions the user is authorized to do may change.
+If the user later authenticates via the AUTHINFO USER/PASS commands,
+the provided username and password are passed to each <auth-program>
+(multiple auth: perl_auth: or python_auth: lines may be present in a
+block; they are run in sequence until one succeeds), if any. If one
+succeeds and returns a different identity than the one assigned at the
+time of the connection, it is matched against the available access
+groups again and the actions the user is authorized to do may change.
When matching auth groups, the last auth group in the file that matches a
given connection and username/password combination is used.
@@ -135,10 +135,10 @@
Just like with auth groups, when matching access groups the last matching
one in the file is used to determine the user's permissions. There is
an exception to this rule: if the auth group which matched the client
-contains the perl_access: parameter then the perl script given as
-argument is used to dynamically generate an access group. This new
-access group is then used to determine the access rights of the
-client; the access groups in the file are ignored.
+contains the perl_access: or python_access: parameter then the script
+given as argument is used to dynamically generate an access group.
+This new access group is then used to determine the access rights of
+the client; the access groups in the file are ignored.
There is one additional special case to be aware of. When forming
particularly complex authentication and authorization rules, it is
@@ -242,13 +242,26 @@
A path to a perl script for authentication. The perl_auth: parameter
works exactly like auth:, except that it calls the named script using
the perl hook rather then an external program. Multiple/mixed use of
-auth: and perl_auth: is permitted within any auth group; each method is
-tried in order listed in F<readers.conf>. perl_auth: has more power
-than auth: in that it provides the authentication program with
-additional information about the client and the ability to return an
-error string. This parameter is only valid if INN is compiled with Perl
-support (B<--with-perl> passed to configure). More information may be
-found in F<doc/hook-perl>.
+auth: perl_auth: and python_auth: is permitted within any auth group;
+each method is tried in order listed in F<readers.conf>. perl_auth:
+has more power than auth: in that it provides the authentication
+program with additional information about the client and the ability
+to return an error string and a username. This parameter is only
+valid if INN is compiled with Perl support (B<--with-perl> passed to
+configure). More information may be found in F<doc/hook-perl>.
+
+=item B<python_auth:>
+
+A python script for authentication. The python_auth: parameter works
+exactly like auth:, except that it calls the named script using the
+python hook rather then an external program. Multiple/mixed use of
+auth: perl_auth: and python_auth: is permitted within any auth group;
+each method is tried in order listed in F<readers.conf>. python_auth:
+has more power than auth: in that it provides the authentication
+program with additional information about the client and the ability
+to return an error string and a username. This parameter is only
+valid if INN is compiled with Python support (B<--with-python> passed
+to configure). More information may be found in F<doc/hook-python>.
=item B<default:>
@@ -293,6 +306,31 @@
support (B<--with-perl> passed to configure). More information may be
found in the file F<doc/hook-perl>.
+=item B<python_access:>
+
+A python script for dynamically generating an access group. If
+an auth group matches successfully and contains a python_access parameter,
+then the argument script will be used to create an access group.
+This group will then determine the access rights of the client,
+overriding any access groups in F<readers.conf>. If and only if a
+successful auth group contains the python_access parameter, F<readers.conf>
+access groups are ignored and the client's rights are instead determined
+dynamically. This parameter is only valid if INN is compiled with Python
+support (B<--with-python> passed to configure). More information may be
+found in the file F<doc/hook-python>.
+
+=item B<python_dynamic:>
+
+A python script for applying access control dynamically on a per
+newsgroup basis. If an auth group matches successfully and contains a
+python_dynamic parameter, then the argument script will be used to
+determine the clients rights each time the user attempts to view a
+newsgroup, read or post an article. Access rights as determined by
+python_dynamic override the values of access group parameters such as
+newsgroups: read: and post:. This parameter is only valid if INN is
+compiled with Python support (B<--with-python> passed to configure).
+More information may be found in the file F<doc/hook-python>.
+
=back
=head1 ACCESS GROUP PARAMETERS
@@ -489,27 +527,28 @@
=item *
When the user authenticates, the auth groups are rescanned, and only the
-matching ones which contain at least one auth: or perl_auth: line are
-considered. These auth groups are scanned from the last to the first,
-running auth: programs and perl_auth: scripts. The first auth group
-(starting from the bottom) to return a valid user is kept as the active
-auth group.
+matching ones which contain at least one auth: perl_auth: or
+python_auth: line are considered. These auth groups are scanned from
+the last to the first, running auth: programs and perl_auth: or
+python_auth: scripts. The first auth group (starting from the bottom)
+to return a valid user is kept as the active auth group.
=item *
Regardless of how an auth group is established, as soon as one is, that
auth group is used to assign a user identity by taking the result of the
-successful res:, auth, or perl_auth: line (or the default: if necessary),
-and appending the default-domain: if necessary. (If the perl_access:
-parameter is present, see below.)
+successful res:, auth, perl_auth: or python_auth: line (or the
+default: if necessary), and appending the default-domain: if
+necessary. (If the perl_access: or python_access: parameter is
+present, see below.)
=item *
Finally, an access group is selected by scanning the access groups from
bottom up and finding the first match. (If the established auth group
-contained a perl_access: line, the dynamically generated access group
-returned by the Perl script is used instead.) User permissions are granted
-based on the established access group.
+contained a perl_access: or python_access line, the dynamically
+generated access group returned by the script is used instead.)
+User permissions are granted based on the established access group.
=back
diff -ur inn/include/inn/innconf.h inn_python/include/inn/innconf.h
--- inn/include/inn/innconf.h Mon Dec 16 02:48:19 2002
+++ inn_python/include/inn/innconf.h Sat Feb 1 11:05:57 2003
@@ -77,8 +77,6 @@
long nfsreaderdelay; /* Delay applied to article arrival */
bool nnrpdcheckart; /* Check article existence before returning? */
long nnrpdloadlimit; /* Maximum getloadvg() we allow */
- bool nnrpperlauth; /* Use Perl for nnrpd authentication */
- bool nnrppythonauth; /* Use Python for nnrpd authentication */
bool noreader; /* Refuse to fork nnrpd for readers? */
bool readerswhenstopped; /* Allow nnrpd when server is paused */
bool readertrack; /* Use the reader tracking system? */
diff -ur inn/lib/innconf.c inn_python/lib/innconf.c
--- inn/lib/innconf.c Sat Jan 18 17:15:34 2003
+++ inn_python/lib/innconf.c Sat Feb 1 11:06:36 2003
@@ -206,7 +206,6 @@
{ K(nnrpdauthsender), BOOL (false) },
{ K(nnrpdloadlimit), NUMBER (16) },
{ K(nnrpdoverstats), BOOL (false) },
- { K(nnrppythonauth), BOOL (false) },
{ K(organization), STRING (NULL) },
{ K(readertrack), BOOL (false) },
{ K(spoolfirst), BOOL (false) },
diff -ur inn/nnrpd/commands.c inn_python/nnrpd/commands.c
--- inn/nnrpd/commands.c Sat Jan 18 20:13:47 2003
+++ inn_python/nnrpd/commands.c Sat Feb 1 11:14:18 2003
@@ -224,9 +224,6 @@
static char Password[SMBUF];
char accesslist[BIG_BUFFER];
char errorstr[BIG_BUFFER];
-#ifdef DO_PYTHON
- int code;
-#endif
if (strcasecmp(av[1], "generic") == 0) {
char *logrec = Glom(av);
@@ -283,69 +280,37 @@
strlcpy(Password, av[2], sizeof(Password));
}
-#ifdef DO_PYTHON
- if (innconf->nnrppythonauth) {
- if ((code = PY_authenticate(ClientHost, ClientIpString, ServerHost, User, Password, accesslist)) < 0) {
- syslog(L_NOTICE, "PY_authenticate(): authentication skipped due to no Python authentication method defined.");
- } else {
- if (code == NNTP_AUTH_OK_VAL) {
- PERMspecified = NGgetlist(&PERMreadlist, accesslist);
- PERMpostlist = PERMreadlist;
- syslog(L_NOTICE, "%s user %s", ClientHost, User);
- if (LLOGenable) {
- fprintf(locallog, "%s user (%s):%s\n", ClientHost, Username, User);
- fflush(locallog);
- }
- Reply("%d Ok\r\n", NNTP_AUTH_OK_VAL);
- /* save these values in case you need them later */
- strlcpy(PERMuser, User, sizeof(PERMuser));
- strlcpy(PERMpass, Password, sizeof(PERMpass));
- PERMneedauth = false;
- PERMauthorized = true;
- return;
- } else {
- syslog(L_NOTICE, "%s bad_auth", ClientHost);
- Reply("%d Authentication error\r\n", NNTP_ACCESS_VAL);
- ExitWithStats(1, false);
- }
- }
- } else {
-#endif /* DO_PYTHON */
-
- if (strcmp(User, PERMuser) == 0 && strcmp(Password, PERMpass) == 0) {
- syslog(L_NOTICE, "%s user %s", ClientHost, PERMuser);
- if (LLOGenable) {
- fprintf(locallog, "%s user (%s):%s\n", ClientHost, Username, PERMuser);
- fflush(locallog);
- }
- Reply("%d Ok\r\n", NNTP_AUTH_OK_VAL);
- PERMneedauth = false;
- PERMauthorized = true;
- return;
- }
-
- errorstr[0] = '\0';
-
- PERMlogin(User, Password, errorstr);
- PERMgetpermissions();
- if (!PERMneedauth) {
- syslog(L_NOTICE, "%s user %s", ClientHost, PERMuser);
- if (LLOGenable) {
- fprintf(locallog, "%s user (%s):%s\n", ClientHost, Username, PERMuser);
- fflush(locallog);
- }
- Reply("%d Ok\r\n", NNTP_AUTH_OK_VAL);
- PERMneedauth = false;
- PERMauthorized = true;
- return;
- }
-#ifdef DO_PYTHON
- }
-#endif /* DO_PYTHON */
+ if (strcmp(User, PERMuser) == 0 && strcmp(Password, PERMpass) == 0) {
+ syslog(L_NOTICE, "%s user %s", ClientHost, PERMuser);
+ if (LLOGenable) {
+ fprintf(locallog, "%s user (%s):%s\n", ClientHost, Username, PERMuser);
+ fflush(locallog);
+ }
+ Reply("%d Ok\r\n", NNTP_AUTH_OK_VAL);
+ PERMneedauth = false;
+ PERMauthorized = true;
+ return;
+ }
+
+ errorstr[0] = '\0';
+
+ PERMlogin(User, Password, errorstr);
+ PERMgetpermissions();
+ if (!PERMneedauth) {
+ syslog(L_NOTICE, "%s user %s", ClientHost, PERMuser);
+ if (LLOGenable) {
+ fprintf(locallog, "%s user (%s):%s\n", ClientHost, Username, PERMuser);
+ fflush(locallog);
+ }
+ Reply("%d Ok\r\n", NNTP_AUTH_OK_VAL);
+ PERMneedauth = false;
+ PERMauthorized = true;
+ return;
+ }
syslog(L_NOTICE, "%s bad_auth", ClientHost);
if (errorstr[0] != '\0') {
- syslog(L_NOTICE, "%s perl error str: %s", ClientHost, errorstr);
+ syslog(L_NOTICE, "%s script error str: %s", ClientHost, errorstr);
Reply("%d %s\r\n", NNTP_ACCESS_VAL, errorstr);
} else {
Reply("%d Authentication error\r\n", NNTP_ACCESS_VAL);
diff -ur inn/nnrpd/group.c inn_python/nnrpd/group.c
--- inn/nnrpd/group.c Sat Jan 18 20:13:47 2003
+++ inn_python/nnrpd/group.c Sun Feb 2 09:54:16 2003
@@ -24,8 +24,13 @@
TOKEN token;
int count;
bool boolval;
+ bool hookpresent = false;
- if (!PERMcanread) {
+#ifdef DO_PYTHON
+ hookpresent = PY_use_dynamic;
+#endif /* DO_PYTHON */
+
+ if (!hookpresent && !PERMcanread) {
Reply("%s\r\n", NOACCESS);
return;
}
@@ -49,17 +54,18 @@
}
#ifdef DO_PYTHON
- if (innconf->nnrppythonauth) {
+ if (PY_use_dynamic) {
char *reply;
- /* Authorize user at a Python authorization module */
- if (PY_authorize(ClientHost, ClientIpString, ServerHost, PERMuser, group, false, &reply) < 0) {
- syslog(L_NOTICE, "PY_authorize(): authorization skipped due to no Python authorization method defined.");
+ /* Authorize user using Python module method dynamic*/
+ if (PY_dynamic(ClientHost, ClientIpString, ServerHost, PERMuser, group, false, &reply) < 0) {
+ syslog(L_NOTICE, "PY_dynamic(): authorization skipped due to no Python dynamic method defined.");
} else {
if (reply != NULL) {
- syslog(L_TRACE, "PY_authorize() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, ClientHost, group, reply);
+ syslog(L_TRACE, "PY_dynamic() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, ClientHost, group, reply);
Reply("%d %s\r\n", NNTP_ACCESS_VAL, reply);
free(group);
+ free(reply);
return;
}
}
@@ -67,18 +73,20 @@
#endif /* DO_PYTHON */
/* If permission is denied, pretend group doesn't exist. */
- if (PERMspecified) {
- grplist[0] = group;
- grplist[1] = NULL;
- if (!PERMmatch(PERMreadlist, grplist)) {
- Reply("%s %s\r\n", NOSUCHGROUP, group);
- free(group);
- return;
- }
- } else {
- Reply("%s %s\r\n", NOSUCHGROUP, group);
- free(group);
- return;
+ if (!hookpresent) {
+ if (PERMspecified) {
+ grplist[0] = group;
+ grplist[1] = NULL;
+ if (!PERMmatch(PERMreadlist, grplist)) {
+ Reply("%s %s\r\n", NOSUCHGROUP, group);
+ free(group);
+ return;
+ }
+ } else {
+ Reply("%s %s\r\n", NOSUCHGROUP, group);
+ free(group);
+ return;
+ }
}
/* Close out any existing article, report group stats. */
diff -ur inn/nnrpd/misc.c inn_python/nnrpd/misc.c
--- inn/nnrpd/misc.c Tue Jan 21 17:22:41 2003
+++ inn_python/nnrpd/misc.c Sun Feb 2 09:54:44 2003
@@ -161,17 +161,19 @@
}
#ifdef DO_PYTHON
- if (innconf->nnrppythonauth) {
+ if (PY_use_dynamic) {
char *reply;
/* Authorize user at a Python authorization module */
- if (PY_authorize(ClientHost, ClientIpString, ServerHost, PERMuser, p, false, &reply) < 0) {
- syslog(L_NOTICE, "PY_authorize(): authorization skipped due to no Python authorization method defined.");
+ if (PY_dynamic(ClientHost, ClientIpString, ServerHost, PERMuser, p, false, &reply) < 0) {
+ syslog(L_NOTICE, "PY_dynamic(): authorization skipped due to no Python dynamic method defined.");
} else {
if (reply != NULL) {
- syslog(L_TRACE, "PY_authorize() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, ClientHost, p, reply);
- return true;
+ syslog(L_TRACE, "PY_dynamic() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, ClientHost, p, reply);
+ free(reply);
+ return false;
}
+ return true;
}
}
#endif /* DO_PYTHON */
diff -ur inn/nnrpd/nnrpd.c inn_python/nnrpd/nnrpd.c
--- inn/nnrpd/nnrpd.c Mon Jan 20 22:39:58 2003
+++ inn_python/nnrpd/nnrpd.c Sat Feb 1 11:27:59 2003
@@ -102,6 +102,10 @@
bool PerlLoaded = false;
#endif /* DO_PERL */
+#ifdef DO_PYTHON
+bool PY_use_dynamic = false;
+#endif /* DO_PYTHON */
+
bool LLOGenable;
static char CMDfetchhelp[] = "[MessageID|Number]";
@@ -235,9 +239,8 @@
SMshutdown();
#ifdef DO_PYTHON
- if (innconf->nnrppythonauth)
- PY_close();
-#endif
+ PY_close_python();
+#endif /* DO_PYTHON */
if (History)
HISclose(History);
@@ -476,11 +479,6 @@
{
struct sockaddr_storage ssc, sss;
socklen_t length;
-#ifdef DO_PYTHON
- char accesslist[BIG_BUFFER];
- int code;
- static ACCESSGROUP *authconf;
-#endif
const char *default_host_error = "unknown error";
ClientIpAddr = 0L;
@@ -581,31 +579,9 @@
strlcpy(LogName, ClientHost, sizeof(LogName));
syslog(L_NOTICE, "%s (%s) connect", ClientHost, ClientIpString);
-#ifdef DO_PYTHON
- if (innconf->nnrppythonauth) {
- if ((code = PY_authenticate(ClientHost, ClientIpString, ServerHost, NULL, NULL, accesslist)) < 0) {
- syslog(L_NOTICE, "PY_authenticate(): authentication skipped due to no Python authentication method defined.");
- } else {
- if (code == 502) {
- syslog(L_NOTICE, "%s no_access", ClientHost);
- Printf("%d You are not in my access file. Goodbye.\r\n",
- NNTP_ACCESS_VAL);
- ExitWithStats(1, true);
- }
- PERMspecified = NGgetlist(&PERMreadlist, accesslist);
- PERMpostlist = PERMreadlist;
- }
- if (!authconf)
- authconf = xmalloc(sizeof(ACCESSGROUP));
- PERMaccessconf = authconf;
- SetDefaultAccess(PERMaccessconf);
- } else {
-#endif /* DO_PYTHON */
- PERMgetaccess(NNRPACCESS);
- PERMgetpermissions();
-#ifdef DO_PYTHON
- }
-#endif /* DO_PYTHON */
+
+ PERMgetaccess(NNRPACCESS);
+ PERMgetpermissions();
}
@@ -773,13 +749,6 @@
static void SetupDaemon(void) {
bool val;
time_t statinterval;
-
-#ifdef DO_PYTHON
- /* Load the Python code */
- if (innconf->nnrppythonauth) {
- PY_setup();
- }
-#endif /* defined(DO_PYTHON) */
val = true;
if (SMsetup(SM_PREOPEN, (void *)&val) && !SMinit()) {
diff -ur inn/nnrpd/nnrpd.h inn_python/nnrpd/nnrpd.h
--- inn/nnrpd/nnrpd.h Sat Jan 18 17:15:35 2003
+++ inn_python/nnrpd/nnrpd.h Sun Feb 2 09:53:54 2003
@@ -274,10 +274,12 @@
#endif /* DO_PERL */
#ifdef DO_PYTHON
-int PY_authenticate(char *clientHost, char *clientIpString, char *serverHost, char *username, char *password, char *accesslist);
-int PY_authorize(char *clientHost, char *clientIpString, char *serverHost, char *username, char *NewsGroup, int PostFlag, char **reply_message);
-void PY_close(void);
-void PY_setup(void);
+extern bool PY_use_dynamic;
+
+int PY_authenticate(char *path, char *clientHost, char *clientIpString, char *serverHost, char *Username, char *Password, char *errorstring, char *newUser);
+void PY_access(char* path, struct vector *access_vec, char *clientHost, char *clientIpString, char *serverHost, char *Username);
+int PY_dynamic(char *clientHost, char *clientIpString, char *ServerHost, char *Username, char *NewsGroup, int PostFlag, char **reply_message);
+void PY_dynamic_init (char* file);
#endif /* DO_PYTHON */
void line_free(struct line *);
diff -ur inn/nnrpd/perl.c inn_python/nnrpd/perl.c
--- inn/nnrpd/perl.c Sat Jan 18 20:13:48 2003
+++ inn_python/nnrpd/perl.c Sat Feb 1 11:30:02 2003
@@ -24,7 +24,7 @@
#include "nntp.h"
/* Skip this entire file if DO_PERL (./configure --with-perl) isn't set. */
-#if DO_PERL
+#ifdef DO_PERL
#include <EXTERN.h>
#include <perl.h>
diff -ur inn/nnrpd/perm.c inn_python/nnrpd/perm.c
--- inn/nnrpd/perm.c Sun Jan 19 12:58:03 2003
+++ inn_python/nnrpd/perm.c Sun Feb 2 09:56:54 2003
@@ -29,7 +29,7 @@
typedef struct _METHOD {
char *name;
char *program;
- int type; /* for seperating perl_auth from auth */
+ int type; /* type of auth (perl, python or external) */
char *users; /* only used for auth_methods, not for res_methods. */
char **extra_headers;
char **extra_logs;
@@ -47,7 +47,10 @@
char *default_user;
char *default_domain;
char *localaddress;
- char *perl_access;
+ char *access_script;
+ int access_type; /* type of access (perl or python) */
+ char *dynamic_script;
+ int dynamic_type; /* type of dynamic authorization (python only) */
} AUTHGROUP;
typedef struct _GROUP {
@@ -150,12 +153,15 @@
#define PERMrejectwith 54
#define PERMmaxbytespersecond 55
#define PERMperl_auth 56
-#define PERMperl_access 57
+#define PERMpython_auth 57
+#define PERMperl_access 58
+#define PERMpython_access 59
+#define PERMpython_dynamic 60
#ifdef HAVE_SSL
-#define PERMrequire_ssl 58
-#define PERMMAX 59
+#define PERMrequire_ssl 61
+#define PERMMAX 62
#else
-#define PERMMAX 58
+#define PERMMAX 61
#endif
#define TEST_CONFIG(a, b) \
@@ -237,7 +243,10 @@
{ PERMrejectwith, "reject_with:" },
{ PERMmaxbytespersecond, "max_rate:" },
{ PERMperl_auth, "perl_auth:" },
+ { PERMpython_auth, "python_auth:" },
{ PERMperl_access, "perl_access:" },
+ { PERMpython_access, "python_access:" },
+ { PERMpython_dynamic, "python_dynamic:" },
#ifdef HAVE_SSL
{ PERMrequire_ssl, "require_ssl:" },
#endif
@@ -375,10 +384,25 @@
else
ret->localaddress = 0;
- if (orig->perl_access)
- ret->perl_access = xstrdup(orig->perl_access);
+ if (orig->access_script)
+ ret->access_script = xstrdup(orig->access_script);
else
- ret->perl_access = 0;
+ ret->access_script = 0;
+
+ if (orig->access_type)
+ ret->access_type = orig->access_type;
+ else
+ ret->access_type = 0;
+
+ if (orig->dynamic_script)
+ ret->dynamic_script = xstrdup(orig->dynamic_script);
+ else
+ ret->dynamic_script = 0;
+
+ if (orig->dynamic_type)
+ ret->dynamic_type = orig->dynamic_type;
+ else
+ ret->dynamic_type = 0;
return(ret);
}
@@ -514,8 +538,10 @@
free(del->default_domain);
if (del->localaddress)
free(del->localaddress);
- if (del->perl_access)
- free(del->perl_access);
+ if (del->access_script)
+ free(del->access_script);
+ if (del->dynamic_script)
+ free(del->dynamic_script);
free(del);
}
@@ -694,6 +720,7 @@
break;
case PERMauth:
case PERMperl_auth:
+ case PERMpython_auth:
case PERMauthprog:
m = xcalloc(1, sizeof(METHOD));
memset(ConfigBit, '\0', ConfigBitsize);
@@ -708,6 +735,13 @@
#else
ReportError(f, "perl_auth can not be used in readers.conf: inn not compiled with perl support enabled.");
#endif
+ } else if (oldtype == PERMpython_auth) {
+#ifdef DO_PYTHON
+ m->type = PERMpython_auth;
+ m->program = xstrdup(tok->name);
+#else
+ ReportError(f, "python_auth can not be used in readers.conf: inn not compiled with python support enabled.");
+#endif
} else {
m->name = xstrdup(tok->name);
tok = CONFgettoken(PERMtoks, f);
@@ -730,11 +764,28 @@
break;
case PERMperl_access:
#ifdef DO_PERL
- curauth->perl_access = xstrdup(tok->name);
+ curauth->access_script = xstrdup(tok->name);
+ curauth->access_type = PERMperl_access;
#else
ReportError(f, "perl_access can not be used in readers.conf: inn not compiled with perl support enabled.");
#endif
break;
+ case PERMpython_access:
+#ifdef DO_PYTHON
+ curauth->access_script = xstrdup(tok->name);
+ curauth->access_type = PERMpython_access;
+#else
+ ReportError(f, "python_access can not be used in readers.conf: inn not compiled with python support enabled.");
+#endif
+ break;
+ case PERMpython_dynamic:
+#ifdef DO_PYTHON
+ curauth->dynamic_script = xstrdup(tok->name);
+ curauth->dynamic_type = PERMpython_dynamic;
+#else
+ ReportError(f, "python_dynamic can not be used in readers.conf: inn not compiled with python support enabled.");
+#endif
+ break;
case PERMlocaladdress:
curauth->localaddress = xstrdup(tok->name);
CompressList(curauth->localaddress);
@@ -1474,7 +1525,7 @@
char *user[2];
static ACCESSGROUP *noaccessconf;
char *uname;
- char *cpp, *perl_path;
+ char *cpp, *script_path;
char **args;
struct vector *access_vec;
@@ -1494,18 +1545,18 @@
SetDefaultAccess(PERMaccessconf);
return;
#ifdef DO_PERL
- } else if (success_auth->perl_access != NULL) {
+ } else if (success_auth->access_script != NULL) {
i = 0;
- cpp = xstrdup(success_auth->perl_access);
+ cpp = xstrdup(success_auth->access_script);
args = 0;
Argify(cpp, &args);
- perl_path = concat(args[0], (char *) 0);
- if ((perl_path != NULL) && (strlen(perl_path) > 0)) {
+ script_path = concat(args[0], (char *) 0);
+ if ((script_path != NULL) && (strlen(script_path) > 0)) {
if(!PerlLoaded) {
loadPerl();
}
- PERLsetup(NULL, perl_path, "access");
- free(perl_path);
+ PERLsetup(NULL, script_path, "access");
+ free(script_path);
uname = xstrdup(PERMuser);
@@ -1516,17 +1567,46 @@
access_realms[0] = xcalloc(1, sizeof(ACCESSGROUP));
- PERMvectortoaccess(access_realms[0], "perl-dyanmic", access_vec);
+ PERMvectortoaccess(access_realms[0], "perl-dynamic", access_vec);
vector_free(access_vec);
} else {
- syslog(L_ERROR, "No script specified in auth method.\n");
+ syslog(L_ERROR, "No script specified in perl_access method.\n");
Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
ExitWithStats(1, true);
}
free(cpp);
free(args);
#endif /* DO_PERL */
+#ifdef DO_PYTHON
+ } else if ((success_auth->access_script != NULL) && (success_auth->access_type == PERMpython_access)) {
+ i = 0;
+ cpp = xstrdup(success_auth->access_script);
+ args = 0;
+ Argify(cpp, &args);
+ script_path = concat(args[0], (char *) 0);
+ if ((script_path != NULL) && (strlen(script_path) > 0)) {
+ uname = xstrdup(PERMuser);
+ access_vec = vector_new();
+
+ PY_access(script_path, access_vec, ClientHost, ClientIpString, ServerHost, uname);
+ free(script_path);
+ free(uname);
+ free(args);
+
+ access_realms[0] = xcalloc(1, sizeof(ACCESSGROUP));
+ memset(access_realms[0], 0, sizeof(ACCESSGROUP));
+
+ PERMvectortoaccess(access_realms[0], "python-dynamic", access_vec);
+
+ vector_free(access_vec);
+ } else {
+ syslog(L_ERROR, "No script specified in python_access method.\n");
+ Reply("%d NNTP server unavailable. Try later.\r\n", NNTP_TEMPERR_VAL);
+ ExitWithStats(1, true);
+ }
+ free(cpp);
+#endif /* DO_PYTHON */
} else {
for (i = 0; access_realms[i]; i++)
;
@@ -1621,6 +1701,12 @@
SetDefaultAccess(PERMaccessconf);
syslog(L_TRACE, "%s no_access_realm", ClientHost);
}
+ /* check if dynamic access control is enabled, if so init it */
+#ifdef DO_PYTHON
+ if ((success_auth->dynamic_type == PERMpython_dynamic) && success_auth->dynamic_script) {
+ PY_dynamic_init(success_auth->dynamic_script);
+ }
+#endif /* DO_PYTHON */
}
/* strip blanks out of a string */
@@ -2131,7 +2217,7 @@
char *arg0;
char *resdir;
char *tmp;
- char *perl_path;
+ char *script_path;
char newUser[BIG_BUFFER];
EXECSTUFF *foo;
int done = 0;
@@ -2148,18 +2234,18 @@
ubuf[0] = '\0';
newUser[0] = '\0';
for (i = 0; auth->auth_methods[i]; i++) {
-#ifdef DO_PERL
if (auth->auth_methods[i]->type == PERMperl_auth) {
+#ifdef DO_PERL
cp = xstrdup(auth->auth_methods[i]->program);
args = 0;
Argify(cp, &args);
- perl_path = concat(args[0], (char *) 0);
- if ((perl_path != NULL) && (strlen(perl_path) > 0)) {
+ script_path = concat(args[0], (char *) 0);
+ if ((script_path != NULL) && (strlen(script_path) > 0)) {
if(!PerlLoaded) {
loadPerl();
}
- PERLsetup(NULL, perl_path, "authenticate");
- free(perl_path);
+ PERLsetup(NULL, script_path, "authenticate");
+ free(script_path);
perlAuthInit();
code = perlAuthenticate(ClientHost, ClientIpString, ServerHost, username, password, errorstr, newUser);
@@ -2183,8 +2269,42 @@
} else {
syslog(L_ERROR, "No script specified in auth method.\n");
}
- } else if (auth->auth_methods[i]->type == PERMauthprog) {
#endif /* DO_PERL */
+ } else if (auth->auth_methods[i]->type == PERMpython_auth) {
+#ifdef DO_PYTHON
+ cp = xstrdup(auth->auth_methods[i]->program);
+ args = 0;
+ Argify(cp, &args);
+ script_path = concat(args[0], (char *) 0);
+ if ((script_path != NULL) && (strlen(script_path) > 0)) {
+ code = PY_authenticate(script_path, ClientHost, ClientIpString, ServerHost, username, password, errorstr, newUser);
+ free(script_path);
+ if (code < 0) {
+ syslog(L_NOTICE, "PY_authenticate(): authentication skipped due to no Python authentication method defined.");
+ } else {
+ if (code == NNTP_AUTH_OK_VAL) {
+ /* Set the value of ubuf to the right username */
+ if (newUser[0] != '\0') {
+ strlcpy(ubuf, newUser, sizeof(ubuf));
+ } else {
+ strlcpy(ubuf, username, sizeof(ubuf));
+ }
+
+ syslog(L_NOTICE, "%s user %s", ClientHost, ubuf);
+ if (LLOGenable) {
+ fprintf(locallog, "%s user %s\n", ClientHost, ubuf);
+ fflush(locallog);
+ }
+ break;
+ } else {
+ syslog(L_NOTICE, "%s bad_auth", ClientHost);
+ }
+ }
+ } else {
+ syslog(L_ERROR, "No script specified in auth method.\n");
+ }
+#endif /* DO_PYTHON */
+ } else {
if (auth->auth_methods[i]->users &&
!MatchUser(auth->auth_methods[i]->users, username))
continue;
@@ -2232,9 +2352,7 @@
if (done)
/* this authenticator succeeded */
break;
-#ifdef DO_PERL
}
-#endif /* DO_PERL */
}
free(resdir);
if (ubuf[0])
diff -ur inn/nnrpd/post.c inn_python/nnrpd/post.c
--- inn/nnrpd/post.c Sat Feb 1 13:23:46 2003
+++ inn_python/nnrpd/post.c Sun Feb 2 09:57:46 2003
@@ -677,6 +677,11 @@
bool IsNewgroup;
bool FoundOne;
int flag;
+ bool hookpresent = false;
+
+#ifdef DO_PYTHON
+ hookpresent = PY_use_dynamic;
+#endif /* DO_PYTHON */
p = HDR(HDR__CONTROL);
IsNewgroup = (p && strncmp(p, "newgroup", 8) == 0);
@@ -692,7 +698,7 @@
do {
if (innconf->mergetogroups && p[0] == 't' && p[1] == 'o' && p[2] == '.')
p = "to";
- if (PERMspecified) {
+ if (!hookpresent && PERMspecified) {
grplist[0] = p;
grplist[1] = NULL;
if (!PERMmatch(PERMpostlist, grplist)) {
@@ -707,16 +713,17 @@
switch (flag) {
case NF_FLAG_OK:
#ifdef DO_PYTHON
- if (innconf->nnrppythonauth) {
+ if (PY_use_dynamic) {
char *reply;
- /* Authorize user at a Python authorization module */
- if (PY_authorize(ClientHost, ClientIpString, ServerHost, PERMuser, p, true, &reply) < 0) {
- syslog(L_NOTICE, "PY_authorize(): authorization skipped due to no Python authorization method defined.");
+ /* Authorize user using Python module method dynamic */
+ if (PY_dynamic(ClientHost, ClientIpString, ServerHost, PERMuser, p, true, &reply) < 0) {
+ syslog(L_NOTICE, "PY_dynamic(): authorization skipped due to no Python dynamic method defined.");
} else {
if (reply != NULL) {
- syslog(L_TRACE, "PY_authorize() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, ClientHost, p, reply);
+ syslog(L_TRACE, "PY_dynamic() returned a refuse string for user %s at %s who wants to read %s: %s", PERMuser, ClientHost, p, reply);
snprintf(Error, sizeof(Error), "%s\r\n", reply);
+ free(reply);
break;
}
}
diff -ur inn/nnrpd/python.c inn_python/nnrpd/python.c
--- inn/nnrpd/python.c Mon Jan 13 22:03:25 2003
+++ inn_python/nnrpd/python.c Sun Feb 2 10:06:38 2003
@@ -15,96 +15,131 @@
#include "inn/innconf.h"
#include "nnrpd.h"
+#include "inn/hashtab.h"
#if defined(DO_PYTHON)
#include "Python.h"
+/* values relate name of hook to array index */
+#define PYTHONauthen 1
+#define PYTHONaccess 2
+#define PYTHONdynamic 3
+
+#define PYTHONtypes_max 4
+
+/* values relate type of method to array index */
+#define PYTHONmain 1
+#define PYTHONinit 2
+#define PYTHONclose 3
+
+#define PYTHONmethods_max 4
+
+/* key names for attributes dictionary */
+#define PYTHONhostname "hostname"
+#define PYTHONipaddress "ipaddress"
+#define PYTHONinterface "interface"
+#define PYTHONuser "user"
+#define PYTHONpass "pass"
+#define PYTHONtype "type"
+#define PYTHONnewsgroup "newsgroup"
+
+/* Max number of items in dictionary to pass to auth methods */
+#define _PY_MAX_AUTH_ITEM 7
+
+
/* Pointers to external Python objects */
PyObject *PYAuthObject = NULL;
-PyObject *PYAuthModule = NULL;
/* Dictionary of params to pass to authentication methods */
PyObject *PYauthinfo = NULL;
PyObject **PYauthitem = NULL;
-PyObject **PYauthkey = NULL;
-/* Max number of items in dictionary to pass to auth methods */
-#define _PY_MAX_AUTH_ITEM 10
-
-/* These are pointers to Python methods specified in auth module */
-PyObject *authenticate_method = NULL;
-PyObject *authorize_method = NULL;
-PyObject *close_method = NULL;
+static PyMethodDef nnrpdPyMethods[];
/* Forward declaration */
static PyObject *PY_set_auth_hook(PyObject *dummy, PyObject *args);
+void PY_load_python(void);
+PyObject* PY_setup(int type, int method, char *file);
+static const void *file_key(const void *p);
+static bool file_equal(const void *k, const void *p);
+static void file_free(void *p);
+static void file_trav(void *data, void* null);
+
+bool PythonLoaded = false;
+
+/* structure for storage of attributes for a module file */
+typedef struct PyFile {
+ char *file;
+ bool loaded[PYTHONtypes_max];
+ PyObject *procs[PYTHONtypes_max][PYTHONmethods_max];
+} PyFile;
-/* These variable defined in other C modules */
-extern char accesslist[];
+/* hash for storing files */
+struct hash *files;
+
+/* for passing the dynamic module filename from perm.c */
+char* dynamic_file;
/*
-** Authenticate connecting host by IP address or username&password.
+** Authenticate connecting host by username&password.
**
** Return NNTP reply code as returned by Python method or -1 if method
** is not defined.
*/
-int PY_authenticate(char *clientHost, char *clientIpString, char *serverHost, char *Username, char *Password, char *accesslist) {
- PyObject *result, *item;
+int PY_authenticate(char* file, char *clientHost, char *clientIpString, char *serverHost, char *Username, char *Password, char *errorstring, char *newUser) {
+ PyObject *result, *item, *proc;
char *type;
int authnum;
int code, i;
+ char *temp;
+
+ PY_load_python();
+ proc = PY_setup(PYTHONauthen, PYTHONmain, file);
/* Return if authentication method is not defined */
- if (authenticate_method == NULL)
+ if (proc == NULL)
return -1;
- /* Figure out authentication type */
- if (Username == NULL)
- type = "connect";
- else
- type = "authinfo";
-
/* Initialize PythonAuthObject with connect method specific items */
authnum = 0;
- /* Authentication type */
- PYauthitem[authnum] = PyBuffer_FromMemory(type, strlen(type));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
-
/* Client hostname */
PYauthitem[authnum] = PyBuffer_FromMemory(clientHost, strlen(clientHost));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
+ PyDict_SetItemString(PYauthinfo, PYTHONhostname, PYauthitem[authnum++]);
/* Client IP number */
PYauthitem[authnum] = PyBuffer_FromMemory(clientIpString, strlen(clientIpString));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
+ PyDict_SetItemString(PYauthinfo, PYTHONipaddress, PYauthitem[authnum++]);
/* Server interface the connection comes to */
PYauthitem[authnum] = PyBuffer_FromMemory(serverHost, strlen(serverHost));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
+ PyDict_SetItemString(PYauthinfo, PYTHONinterface, PYauthitem[authnum++]);
/* Username if known */
- if (Username == NULL)
+ if (Username == NULL) {
PYauthitem[authnum] = Py_None;
- else
+ } else {
PYauthitem[authnum] = PyBuffer_FromMemory(Username, strlen(Username));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
+ }
+ PyDict_SetItemString(PYauthinfo, PYTHONuser, PYauthitem[authnum++]);
/* Password if known */
- if (Password == NULL)
+ if (Password == NULL) {
PYauthitem[authnum] = Py_None;
- else
+ } else {
PYauthitem[authnum] = PyBuffer_FromMemory(Password, strlen(Password));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
+ }
+ PyDict_SetItemString(PYauthinfo, PYTHONpass, PYauthitem[authnum++]);
/* Now invoke authenticate method and see if it likes this user */
- result = PyObject_CallFunction(authenticate_method, "O", PYauthinfo);
+ result = PyObject_CallFunction(proc, "O", PYauthinfo);
/* Check the response */
- if (result == NULL || !PyTuple_Check(result))
+ if (result == NULL || !PyTuple_Check(result)
+ || ((PyTuple_Size(result) != 2) && (PyTuple_Size(result) != 3)))
{
- syslog(L_ERROR, "python authenticate_method (type %s) returned wrong result", type);
+ syslog(L_ERROR, "python authenticate method returned wrong result");
Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
ExitWithStats(1, true);
}
@@ -115,7 +150,7 @@
/* Check the item */
if (!PyInt_Check(item))
{
- syslog(L_ERROR, "python authenticate_method (type %s) returned bad NNTP response code", type);
+ syslog(L_ERROR, "python authenticate method returned bad NNTP response code");
Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
ExitWithStats(1, true);
}
@@ -123,165 +158,254 @@
/* Store the code */
code = PyInt_AS_LONG(item);
- /* Get the CanPost setting */
+ /* Get the error string */
item = PyTuple_GetItem(result, 1);
/* Check the item */
- if (!PyInt_Check(item))
+ if (!PyString_Check(item))
{
- syslog(L_ERROR, "python authenticate_method (type %s) returned bad CanPost setting", type);
+ syslog(L_ERROR, "python authenticate method returned bad error string");
Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
ExitWithStats(1, true);
}
- /* Store the setting */
- PERMcanpost = PyInt_AS_LONG(item);
+ /* Store error string */
+ temp = PyString_AS_STRING(item);
+ errorstring = xstrdup(temp);
+
+ if (PyTuple_Size(result) == 3) {
+
+ /* Get the username string */
+ item = PyTuple_GetItem(result, 2);
+
+ /* Check the item */
+ if (!PyString_Check(item)) {
+ syslog(L_ERROR, "python authenticate method returned bad username string");
+ Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
+ ExitWithStats(1, true);
+ }
+
+ /* Store error string */
+ temp = PyString_AS_STRING(item);
+ newUser = xstrdup(temp);
+ }
- /* Get the CanRead setting */
- item = PyTuple_GetItem(result, 2);
+ /* Clean up the dictionary object */
+ PyDict_Clear(PYauthinfo);
- /* Check the item */
- if (!PyInt_Check(item))
- {
- syslog(L_ERROR, "python authenticate_method (type %s) returned bad CanRead setting", type);
- Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
- ExitWithStats(1, true);
+ /* Clean up dictionary items */
+ for (i = 0; i < authnum; i++) {
+ if (PYauthitem[i] != Py_None) {
+ Py_DECREF(PYauthitem[i]);
+ }
}
- /* Store the setting */
- PERMcanread = PyInt_AS_LONG(item);
+ /* Log auth result */
+ syslog(L_NOTICE, "python authenticate method succeeded, return code %d, error string %s", code, errorstring);
- /* Get the access list */
- item = PyTuple_GetItem(result, 3);
+ /* Return response code */
+ return code;
+}
- /* Check the item */
- if (!PyString_Check(item))
- {
- syslog(L_ERROR, "python authenticate_method (type %s) returned bad access list value", type);
- Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
- ExitWithStats(1, true);
- }
+/*
+** Create an access group based on the values returned by the script in file
+**
+*/
+void PY_access(char* file, struct vector *access_vec, char *clientHost, char *clientIpString, char *serverHost, char *Username) {
+ PyObject *result, *key, *value, *proc;
+ char *skey, *svalue, *temp;
+ int authnum;
+ int i;
- /* Store access list*/
- strcpy(accesslist, PyString_AS_STRING(item));
+ PY_load_python();
+ proc = PY_setup(PYTHONaccess, PYTHONmain, file);
- /* Fix the NNTP response code */
- if ((code == NNTP_POSTOK_VAL) || (code == NNTP_NOPOSTOK_VAL))
- {
- code = PERMcanpost ? NNTP_POSTOK_VAL : NNTP_NOPOSTOK_VAL;
- }
+ /* Exit if access method is not defined */
+ if (proc == NULL) {
+ syslog(L_ERROR, "python access method not defined");
+ Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
+ ExitWithStats(1, true);
+ }
+
+ /* Initialize PythonAuthObject with group method specific items */
+ authnum = 0;
+
+ /* Client hostname */
+ PYauthitem[authnum] = PyBuffer_FromMemory(clientHost, strlen(clientHost));
+ PyDict_SetItemString(PYauthinfo, PYTHONhostname, PYauthitem[authnum++]);
+
+ /* Client IP number */
+ PYauthitem[authnum] = PyBuffer_FromMemory(clientIpString, strlen(clientIpString));
+ PyDict_SetItemString(PYauthinfo, PYTHONipaddress, PYauthitem[authnum++]);
- /* Initialize needauth flag */
- if (code == NNTP_AUTH_NEEDED_VAL)
- PERMneedauth = true;
+ /* Server interface the connection comes to */
+ PYauthitem[authnum] = PyBuffer_FromMemory(serverHost, strlen(serverHost));
+ PyDict_SetItemString(PYauthinfo, PYTHONinterface, PYauthitem[authnum++]);
+ /* Username */
+ PYauthitem[authnum] = PyBuffer_FromMemory(Username, strlen(Username));
+ PyDict_SetItemString(PYauthinfo, PYTHONuser, PYauthitem[authnum++]);
+
+ /* Password is not known */
+ PYauthitem[authnum] = Py_None;
+ PyDict_SetItemString(PYauthinfo, PYTHONpass, PYauthitem[authnum++]);
+
+ /*
+ * Now invoke newsgroup access method
+ */
+ result = PyObject_CallFunction(proc, "O", PYauthinfo);
+
+ /* Check the response */
+ if (result == NULL || result == Py_None || !PyDict_Check(result)) {
+ syslog(L_ERROR, "python access method returned wrong result - expected a dictionary");
+ Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
+ ExitWithStats(1, true);
+ }
+
+ /* resize vector to dictionary length */
+ vector_resize(access_vec, PyDict_Size(result) - 1);
+
+ /* store dict values in proper format in access vector */
+ i = 0;
+ while(PyDict_Next(result, &i, &key, &value)) {
+ if (!PyString_Check(key)) {
+ syslog(L_ERROR, "python access method return dictionary key %i not a string", i);
+ Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
+ ExitWithStats(1, false);
+ }
+ if (!PyString_Check(value)) {
+ syslog(L_ERROR, "python access method return dictionary value %i not a string", i);
+ Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
+ ExitWithStats(1, false);
+ }
+
+ temp = PyString_AsString(key);
+ skey = xstrdup(temp);
+
+ temp = PyString_AsString(value);
+ svalue = xstrdup(temp);
+
+ skey = strcat(skey, ": \"");
+ skey = strcat(skey, svalue);
+ skey = strcat(skey, "\"\n");
+ vector_add(access_vec, skey);
+
+ free(skey);
+ free(svalue);
+ }
+
/* Clean up the dictionary object */
PyDict_Clear(PYauthinfo);
-
/* Clean up dictionary items */
- for (i = 0; i < authnum; i++)
- {
- if (PYauthitem[i] != Py_None)
- {
- Py_DECREF(PYauthitem[i]);
+ for (i = 0; i < authnum; i++) {
+ if (PYauthitem[i] != Py_None) {
+ Py_DECREF(PYauthitem[i]);
}
}
/* Log auth result */
- syslog(L_NOTICE, "python authenticate_method (type %s) succeeded, return code %d", type, code);
+ syslog(L_NOTICE, "python access method succeeded");
+}
- /* Return response code */
- return code;
+/*
+** Initialize dynamic access control code
+*/
+
+void PY_dynamic_init (char* file) {
+ dynamic_file = xstrdup(file);
+ PY_use_dynamic = true;
}
/*
-** Authorize user access to a newsgroup.
+** Determine dynamic user access rights to a given newsgroup.
**
** Return 0 if requested privelege is granted or positive value
** and a reply_message pointer initialized with reply message.
-** Return negative value if authorize method is not defined.
+** Return negative value if dynamic method is not defined.
*/
-int PY_authorize(char *clientHost, char *clientIpString, char *serverHost, char *Username, char *NewsGroup, int PostFlag, char **reply_message) {
- PyObject *result, *item;
- char *string;
+int PY_dynamic(char *clientHost, char *clientIpString, char *serverHost, char *Username, char *NewsGroup, int PostFlag, char **reply_message) {
+ PyObject *result, *item, *proc;
+ char *string, *temp;
int authnum;
int i;
- /* Return if authorize_method is not defined */
- if (authorize_method == NULL)
+ PY_load_python();
+ proc = PY_setup(PYTHONdynamic, PYTHONmain, dynamic_file);
+
+ /* Return if dynamic method is not defined */
+ if (proc == NULL)
return -1;
/* Initialize PythonAuthObject with group method specific items */
authnum = 0;
- /* Assign authentication type */
- PYauthitem[authnum] = PyBuffer_FromMemory(PostFlag ? "post" : "read", 4);
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
-
/* Client hostname */
PYauthitem[authnum] = PyBuffer_FromMemory(clientHost, strlen(clientHost));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
-
+ PyDict_SetItemString(PYauthinfo, PYTHONhostname, PYauthitem[authnum++]);
+
/* Client IP number */
PYauthitem[authnum] = PyBuffer_FromMemory(clientIpString, strlen(clientIpString));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
-
+ PyDict_SetItemString(PYauthinfo, PYTHONipaddress, PYauthitem[authnum++]);
+
/* Server interface the connection comes to */
PYauthitem[authnum] = PyBuffer_FromMemory(serverHost, strlen(serverHost));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
-
+ PyDict_SetItemString(PYauthinfo, PYTHONinterface, PYauthitem[authnum++]);
+
/* Username */
PYauthitem[authnum] = PyBuffer_FromMemory(Username, strlen(Username));
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
-
+ PyDict_SetItemString(PYauthinfo, PYTHONuser, PYauthitem[authnum++]);
+
/* Password is not known */
PYauthitem[authnum] = Py_None;
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
+ PyDict_SetItemString(PYauthinfo, PYTHONpass, PYauthitem[authnum++]);
+ /* Assign authentication type */
+ PYauthitem[authnum] = PyBuffer_FromMemory(PostFlag ? "post" : "read", 4);
+ PyDict_SetItemString(PYauthinfo, PYTHONtype, PYauthitem[authnum++]);
+
/* Newsgroup user tries to access */
- PYauthitem[authnum] = PyBuffer_FromMemory(NewsGroup, strlen(NewsGroup));;
- PyDict_SetItem(PYauthinfo, PYauthkey[authnum], PYauthitem[authnum++]);
-
+ PYauthitem[authnum] = PyBuffer_FromMemory(NewsGroup, strlen(NewsGroup));
+ PyDict_SetItemString(PYauthinfo, PYTHONnewsgroup, PYauthitem[authnum++]);
+
/*
- * Now invoke newsgroup access authorization method and see if
+ * Now invoke newsgroup dynamic access method and see if
* it likes this user to access this newsgroup.
*/
- result = PyObject_CallFunction(authorize_method, "O", PYauthinfo);
+ result = PyObject_CallFunction(proc, "O", PYauthinfo);
/* Check the response */
if (result == NULL || result != Py_None && !PyString_Check(result))
{
- syslog(L_ERROR, "python authorize_method (%s access) returned wrong result", PostFlag ? "post" : "read");
+ syslog(L_ERROR, "python dyanmic method (%s access) returned wrong result: %s", PostFlag ? "post" : "read", result);
Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
ExitWithStats(1, false);
}
/* Get the response string */
- if (result == Py_None)
+ if (result == Py_None) {
string = NULL;
- else
- string = PyString_AS_STRING(result);
-
+ } else {
+ temp = PyString_AS_STRING(result);
+ string = xstrdup(temp);
+ }
/* Clean up the dictionary object */
PyDict_Clear(PYauthinfo);
/* Clean up dictionary items */
- for (i = 0; i < authnum; i++)
- {
- if (PYauthitem[i] != Py_None)
- {
- Py_DECREF(PYauthitem[i]);
- }
+ for (i = 0; i < authnum; i++) {
+ if (PYauthitem[i] != Py_None) {
+ Py_DECREF(PYauthitem[i]);
+ }
}
/* Log auth result */
- syslog(L_NOTICE, "python authorize_method (%s access) succeeded, refusion string: %s", PostFlag ? "post" : "read", string == NULL ? "<empty>" : string);
+ syslog(L_NOTICE, "python dynamic method (%s access) succeeded, refusion string: %s", PostFlag ? "post" : "read", string == NULL ? "<empty>" : string);
/* Initialize reply string */
if (reply_message != NULL)
*reply_message = string;
-
+
/* Return result */
return string == NULL ? 0 : 1;
}
@@ -291,16 +415,35 @@
** This runs when nnrpd shuts down.
*/
void
-PY_close(void)
+PY_close_python(void)
{
- PyObject *result;
+ hash_traverse(files, file_trav, NULL);
- if (close_method != NULL) {
- result = PyObject_CallFunction(close_method, NULL);
- Py_XDECREF(result);
- }
+ hash_free(files);
+
+ free(dynamic_file);
}
+/*
+** Traversal function for PY_close_python
+*/
+void
+file_trav(void *data, void* null)
+{
+ PyFile *fp = data;
+ int j;
+ PyObject *result, *func;
+
+ for (j = 1; j < PYTHONtypes_max; j++) {
+ if (fp->loaded[j] != false) {
+ func = fp->procs[j][PYTHONclose];
+ if (func != NULL) {
+ result = PyObject_CallFunction(func, NULL);
+ Py_XDECREF(result);
+ }
+ }
+ }
+}
/*
** Python's syslog module isn't compiled in by default. It's easier
@@ -375,17 +518,60 @@
return result;
}
+/*
+** Load the Python interpreter
+*/
+void PY_load_python() {
+ int i, authnum;
+
+ if (!PythonLoaded) {
+ /* add path for nnrpd module */
+ setenv("PYTHONPATH", innconf->pathfilter, 1);
+
+ /* Load up the interpreter ;-O */
+ Py_Initialize();
+
+ /* It makes Python sad when its stdout and stderr are closed. */
+ if (feof(stdout) || feof(stderr))
+ PyRun_SimpleString("import sys; sys.stdout=sys.stderr=open('/dev/null', 'a')");
+
+ /* See if Python initialized OK */
+ if (!Py_IsInitialized ()) {
+ syslog(L_ERROR, "python interpreter NOT initialized");
+ return;
+ }
+
+
+ /* Build a module interface to certain nnrpd functions */
+ (void) Py_InitModule("nnrpd", nnrpdPyMethods);
+
+ /*
+ ** Grab space for authinfo dictionary so we aren't forever
+ ** recreating them.
+ */
+ PYauthinfo = PyDict_New();
+ PYauthitem = xcalloc(_PY_MAX_AUTH_ITEM, sizeof(PyObject *));
+
+ /* create hash to store file attributes */
+
+ files = hash_create(4, hash_string, file_key,
+ file_equal, file_free);
+
+ PythonLoaded = true;
+
+ syslog(L_NOTICE, "python interpreter initialized OK");
+ }
+}
/*
** Check that a method exists and is callable. Set up a pointer to
** the corresponding PyObject, or NULL if not found.
*/
void
-PYdefonemethod(methptr, methname)
- PyObject **methptr;
- char *methname;
-{
- Py_XDECREF(*methptr);
+PYdefonemethod(PyFile *fp, int type, int method, char *methname) {
+ PyObject **methptr;
+
+ methptr = &fp->procs[type][method];
/* Get a pointer to given method */
*methptr = PyObject_GetAttrString(PYAuthObject, methname);
@@ -405,82 +591,131 @@
/*
-** Look up all the known authentication/authorization methods and set up
+** Look up all the known python methods and set up
** pointers to them so that we could call them from nnrpd.
*/
void
-PYdefmethods(void)
+PYdefmethods(PyFile *fp)
{
/* Get a reference to authenticate() method */
- PYdefonemethod(&authenticate_method, "authenticate");
-
- /* Get a reference to authorize() method */
- PYdefonemethod(&authorize_method, "authorize");
+ PYdefonemethod(fp, PYTHONauthen, PYTHONmain, "authenticate");
- /* Get a reference to close() method */
- PYdefonemethod(&close_method, "close");
+ /* Get a reference to authen_init() method */
+ PYdefonemethod(fp, PYTHONauthen, PYTHONinit, "authen_init");
+
+ /* Get a reference to authen_close() method */
+ PYdefonemethod(fp, PYTHONauthen, PYTHONclose, "authen_close");
+
+ /* Get a reference to access() method */
+ PYdefonemethod(fp, PYTHONaccess, PYTHONmain, "access");
+
+ /* Get a reference to access_init() method */
+ PYdefonemethod(fp, PYTHONaccess, PYTHONinit, "access_init");
+
+ /* Get a reference to access_close() method */
+ PYdefonemethod(fp, PYTHONaccess, PYTHONclose, "access_close");
+
+ /* Get a reference to dynamic() method */
+ PYdefonemethod(fp, PYTHONdynamic, PYTHONmain, "dynamic");
+
+ /* Get a reference to dynamic_init() method */
+ PYdefonemethod(fp, PYTHONdynamic, PYTHONinit, "dynamic_init");
+
+ /* Get a reference to dynamic_close() method */
+ PYdefonemethod(fp, PYTHONdynamic, PYTHONclose, "dynamic_close");
}
/*
-** Called when nnrpd starts -- this gets the scripts hooked in.
+** Called when a python hook is needed -- this gets the scripts hooked in.
*/
-void
-PY_setup(void)
+PyObject*
+PY_setup(int type, int method, char *file)
{
- int authnum;
+ int i;
+ PyFile *fp;
+ char *temp;
+ PyObject *result;
+
+ /* check to see if this file is in files */
+ if (!(hash_lookup(files, file))) {
+ fp = xmalloc(sizeof(PyFile));
+ fp->file = xstrdup(file);
+
+ for (i = 1; i < PYTHONtypes_max; i++) {
+ fp->loaded[i] = false;
+ }
+
+ /* Load up external module */
+ (void) PyImport_ImportModule(file);
+
+ /* See if nnrpd auth object is defined in auth module */
+ if (PYAuthObject == NULL) {
+ syslog(L_ERROR, "python auth object is not defined");
+ Reply("%d Internal Error (7). Goodbye\r\n", NNTP_ACCESS_VAL);
+ PY_close_python();
+ ExitWithStats(1, false);
+ } else {
+ /* Set up pointers to known Python methods */
+ PYdefmethods(fp);
+ }
+ hash_insert(files, file, fp);
+
+ if ((!fp->loaded[type]) && (fp->procs[type][PYTHONinit] != NULL)) {
+ result = PyObject_CallFunction(fp->procs[type][PYTHONinit], NULL);
+ if (result != NULL) {
+ Py_XDECREF(result);
+ }
+ fp->loaded[type] = true;
+ }
+ return fp->procs[type][method];
+ }
+}
- /* Export $PYTHONPATH to let Python find the scripts */
- setenv("PYTHONPATH", innconf->pathfilter, 1);
+/*
+** Return the key (filename) from a file struct, used by the hash table.
+*/
+static const void *
+file_key(const void *p)
+{
+ const struct PyFile *f = p;
- /* Load up the interpreter ;-O */
- Py_Initialize();
+ return f->file;
+}
- /* It makes Python sad when its stdout and stderr are closed. */
- if (feof(stdout) || feof(stderr))
- PyRun_SimpleString
- ("import sys; sys.stdout=sys.stderr=open('/dev/null', 'a')");
+/*
+** Check to see if a provided key matches the key of a PyFile struct,
+** used by the hash table.
+*/
+static bool
+file_equal(const void *k, const void *p)
+{
+ const char *key = k;
+ const struct PyFile *f = p;
- /* See it Python initialized OK */
- if (!Py_IsInitialized ()) {
- syslog(L_ERROR, "python interpreter NOT initialized");
- return;
- }
- syslog(L_NOTICE, "python interpreter initialized OK");
+ return strcmp(key, f->file) == 0;
+}
- /* Build a module interface to certain nnrpd functions */
- Py_InitModule("nnrpd", nnrpdPyMethods);
+/*
+** Free a file, used by the hash table.
+*/
+static void
+file_free(void *p)
+{
+ struct PyFile *fp = p;
+ int i, j;
- /* Load up external nntpd auth module */
- PYAuthModule = PyImport_ImportModule(_PATH_PYTHON_AUTH_M);
- if (PYAuthModule == NULL)
- syslog(L_ERROR, "failed to import external python module");
+ free(fp->file);
- /* See if nnrpd auth object is defined in auth module */
- if (PYAuthObject == NULL)
- syslog(L_ERROR, "python auth object is not defined");
- else {
- /* Set up pointers to known Python methods */
- PYdefmethods();
- syslog(L_NOTICE, "some python methods defined. good.");
+ for (i = 1; i < PYTHONtypes_max; i++) {
+ for (j = 1; j < PYTHONmethods_max; j++) {
+ if (fp->procs[i][j] != NULL) {
+ Py_DECREF(fp->procs[i][j]);
+ }
+ }
}
- /*
- ** Grab space for authinfo dictionary so we aren't forever
- ** recreating them.
- */
- PYauthinfo = PyDict_New();
- PYauthitem = xmalloc(_PY_MAX_AUTH_ITEM * sizeof(PyObject *));
- PYauthkey = xmalloc(_PY_MAX_AUTH_ITEM * sizeof(PyObject *));
-
- /* Preallocate keys for the authinfo dictionary (up to PY_MAX_AUTH_ITEM) */
- authnum = 0;
- PYauthkey[authnum++] = PyString_InternFromString("type");
- PYauthkey[authnum++] = PyString_InternFromString("hostname");
- PYauthkey[authnum++] = PyString_InternFromString("ipaddress");
- PYauthkey[authnum++] = PyString_InternFromString("interface");
- PYauthkey[authnum++] = PyString_InternFromString("user");
- PYauthkey[authnum++] = PyString_InternFromString("pass");
- PYauthkey[authnum++] = PyString_InternFromString("newsgroup");
+ free(fp);
}
+
#endif /* defined(DO_PYTHON) */
diff -ur inn/samples/inn.conf.in inn_python/samples/inn.conf.in
--- inn/samples/inn.conf.in Mon Dec 23 21:01:36 2002
+++ inn_python/samples/inn.conf.in Sat Feb 1 11:08:15 2003
@@ -77,7 +77,6 @@
initialtimeout: 10
msgidcachesize: 10000
nnrpdcheckart: true
-nnrppythonauth: false
noreader: false
readerswhenstopped: false
readertrack: false
diff -ur inn/samples/nnrpd_auth.py inn_python/samples/nnrpd_auth.py
--- inn/samples/nnrpd_auth.py Mon Jan 15 05:31:35 2001
+++ inn_python/samples/nnrpd_auth.py Sat Feb 1 11:10:20 2003
@@ -1,44 +1,53 @@
#
#
-# This is a sample authentication and authorization module for nnrpd hook
+# This is a sample authentication and authorization module for python
+# nnrpd hook
#
# For details, see the file doc/hook-python that came with INN.
#
#
-# This file is loaded when nnrpd starts up. An instance of AUTH class
-# is passed to nnrpd via set_auth_hook() function imported from nnrpd. The
-# following methods of that class are known to nnrpd:
-#
-# __init__() - Called on nnrpd startup. Use this method
-# to initilalize your variables or open
-# a database connection.
-# close() - Called on nnrpd termination. Save your state
-# variables or close a database connection.
-# authenticate() - Called whenever a reader connects or uses
-# AUTHINFO command. A "type" entry of
-# "attributes" dictionary is passed to this
-# method to figure out the type of
-# authentication happening.
-# authorize() - Called whenever a reader requests either
-# read or post access to a newsgroup.
-#
-# Attributes about the connection are passed to the program in the
-# "attributes" global dictinary variable.
-#
-# The authenticate() method should return a tuple of four elements:
-#
-# 1) NNTP response code. Should be one of the codes from
-# NNRPD_AUTH.connectcodes{} or NNRPD_AUTH.authcodes{}
-# 2) Reading Allowed. Should be a boolean value.
-# 3) Posting Allowed. Should be a boolean value.
-# 4) Wildmat expression that says what groups to provide access to.
-#
-# All four of these are required.
-#
-# The authorize() method should return None to grant requested
-# priveleges or a non-empty string (which will be reported back to reader)
-# otherwise.
+# This file is loaded when one of the python_* readers.conf parameters
+# is encountered. An instance of AUTH class is passed to nnrpd via
+# set_auth_hook() function imported from nnrpd. The following methods
+# of that class are known to nnrpd:
+#
+# __init__() - Use this method to initilalize your
+# general variables or open a common
+# database connection. May be omitted.
+# access_init() - Init function specific to access
+# control. May be omitted
+# access(attributes) - Called when a python_access
+# statement is reached in the
+# processing of readers.conf. Returns
+# a dictionary of values representing
+# statements to be included in an
+# access group.
+# access_close() - Called on nnrpd termination. Save
+# your state variables or close a
+# database connection. May be omitted
+# authen_init() - Init function specific to
+# authentication. May be omitted
+# authenticate(attributes) - Called when a python_auth statement
+# is reached in the processing of
+# readers.conf. Returns a response
+# code, an error string and an
+# optional string to appear in the
+# logs as the username.
+# authen_close() - Called on nnrpd termination. Save
+# your state variables or close a database
+# connection. May be omitted
+# dynamic_init() - Init function specific to
+# authentication. May be omitted
+# dynamic(attributes) - Called whenever a reader requests either
+# read or post access to a
+# newsgroup. Returns None to grant
+# access, or a non-empty string (which
+# will be reported back to reader)
+# otherwise.
+# dynamic_close() - Called on nnrpd termination. Save
+# your state variables or close a database
+# connection. May be omitted
#
# If there is a problem with return codes from any of these methods then nnrpd
# will die and syslog the exact reason.
@@ -59,8 +68,8 @@
class AUTH:
"""Provide authentication and authorization callbacks to nnrpd."""
def __init__(self):
- """This runs on nnrpd startup. It is a good place to initialize
- variables or open a database connection.
+ """This is a good place to initialize variables or open a
+ database connection.
"""
# Create a list of NNTP codes to respond on connect
self.connectcodes = { 'READPOST':200,
@@ -76,65 +85,66 @@
syslog('notice', 'nnrpd authentication class instance created')
- def close(self):
- """Runs when nnrpd exits. You can use this method to save state
- information to be restored by the __init__() method or close
- a database connection.
- """
- syslog('notice', "close method running, bye!")
-
def authenticate(self, attributes):
- """Called when a reader connects or authenticates"""
+ """Called when python_auth is encountered in readers.conf"""
# just for debugging purposes
- syslog('debug', 'authenticate() invoked against type %s, hostname %s, ipaddress %s, interface %s, user %s' % (\
- attributes['type'], \
+ syslog('notice', 'n_a authenticate() invoked: hostname %s, ipaddress %s, interface %s, user %s' % (\
attributes['hostname'], \
attributes['ipaddress'], \
attributes['interface'], \
attributes['user']))
- # allow newsreading from specific host only
- if attributes['type'] == buffer('connect'):
- if attributes['ipaddress'] == buffer('127.0.0.1'):
- syslog('notice', 'authentication by IP address succeeded')
- return ( self.connectcodes['READPOST'], 1, 1, '*' )
- else:
- syslog('notice', 'authentication by IP address failed')
- return ( self.connectcodes['PERMDENIED'], 0, 0, '!*' )
-
- # do not do any username authentication
- elif attributes['type'] == buffer('authinfo'):
+ # do username passworld authentication
+ if 'foo' == str(attributes['user']) \
+ and 'foo' == str(attributes['pass']):
syslog('notice', 'authentication by username succeeded')
- return ( self.authcodes['ALLOWED'], 1, 1, '*')
+ return ( self.authcodes['ALLOWED'], 'No error', 'default_user')
else:
- syslog('notice', 'authentication type is not known: %s' % attributes['type'])
- return ( self.authcodes['DENIED'], 0, 0, '!*')
+ syslog('notice', 'authentication by username failed')
+ return ( self.authcodes['DENIED'], 'Access Denied!')
- def authorize(self, attributes):
- """Called when a reader requests either read or post permission
- for particular newsgroup.
+ def access(self, attributes):
+ """Called when python_access is encountered in readers.conf"""
+
+ # just for debugging purposes
+ syslog('notice', 'n_a access() invoked: hostname %s, ipaddress %s, interface %s, user %s' % (\
+ attributes['hostname'], \
+ attributes['ipaddress'], \
+ attributes['interface'], \
+ attributes['user']))
+
+ # allow newsreading from specific host only
+ if '127.0.0.1' == str(attributes['ipaddress']):
+ syslog('notice', 'authentication by IP address succeeded')
+ return {'read':'*','post':'*'}
+ else:
+ syslog('notice', 'authentication by IP address failed')
+ return {'read':'!*','post':'!*'}
+
+ def dynamic(self, attributes):
+ """Called when python_dynamic was reached in the processing of
+ readers.conf and a reader requests either read or post
+ permission for particular newsgroup.
"""
# just for debugging purposes
- syslog('debug', 'authorize() invoked against type %s, hostname %s, ipaddress %s, interface %s, user %s, newsgroup %s' % (\
+ syslog('notice', 'n_a dyanmic() invoked against type %s, hostname %s, ipaddress %s, interface %s, user %s' % (\
attributes['type'], \
attributes['hostname'], \
attributes['ipaddress'], \
attributes['interface'], \
- attributes['user'], \
- attributes['newsgroup']))
+ attributes['user']))
- # Allow reading of any newsgroup
- if attributes['type'] == buffer('read'):
- syslog('notice', 'authorization for read access succeeded')
+ # Allow reading of any newsgroup but not posting
+ if 'post' == str(attributes['type']):
+ syslog('notice', 'authorization for post access denied')
+ return "no posting for you"
+ elif 'read' == str(attributes['type']):
+ syslog('notice', 'authorization for read access granted')
return None
- # ..but disallow postings
- elif attributes['type'] == buffer('post'):
- syslog('notice', 'authorization for post access failed')
- return "Posting disallowed"
else:
syslog('notice', 'authorization type is not known: %s' % attributes['type'])
- return "Internal error"
+ return "Internal error";
********************* end diff ***********************
--
erik | "It is idle to think that, by means of words, | Maurice
kl at von | any real communication can ever pass | Maeterlinck
eriq.org | from one [human] to another." | Silence
More information about the inn-workers
mailing list