ADDED cgi-bin/auth.py Index: cgi-bin/auth.py ================================================================== --- cgi-bin/auth.py +++ cgi-bin/auth.py @@ -0,0 +1,73 @@ +#!/usr/bin/python +# +# auth.py +# Deal with cookies, passwords, and all that stuff +# +import os, Cookie +from passlib.hash import md5_crypt +import webcal + +# CGI environment variable for cookies, and the one we want +CGICOOK = "HTTP_COOKIE" +SESSCOOK = "webcalendar_session" + +# Set up, when we're invoked as a CGI under a web server +# Return None for failure, or the authenticated user name +def authen_cookie(): + + # Pull cookie which proves who we are + if CGICOOK not in os.environ: + # Didn't present any cookies + return None + cookies = Cookie.SimpleCookie(os.environ[CGICOOK]) + if SESSCOOK not in cookies: + # Didn't present a session cookie + return None + cookie = cookies[SESSCOOK].value + tup = webcal.decode_string(cookie).split("|") + if (not tup) or (len(tup) != 2): + # Malformed cookie + return None + uname,cryptpw = tup + + # Pull our DB record for this guy + c = webcal.db.cursor() + c.execute("select cal_passwd from webcal_user where cal_login = %s", + (uname,)) + tup = c.fetchone() + c.close() + if tup is None: + return None + dbpw = tup[0] + + # Tell if it matches + if not md5_crypt.verify(dbpw, cryptpw): + return None + + return uname + +# Run this as a part of the CGI startup. It'll push out a +# redir (so they can log in) and return None, otherwise +# it'll return the authenticated user's name +def init_cgi(): + # Common header + print """Content-Type: text/html + + +""" + + # Authentication + uname = authen_cookie() + if uname is None: + print """ + Bad Webcalendar Session + + + + Redirecting you to Webcalendar for login... + + + """ + return None + return uname + Index: cgi-bin/today.py ================================================================== --- cgi-bin/today.py +++ cgi-bin/today.py @@ -3,53 +3,18 @@ # today.py # Display events for today # import sys, time, os from webcal import cmp_time -import webcal +import webcal, auth # By default, display events for Today Target = webcal.Today # Filled in by authen (or debug) uname = None -# Common HTTP response header -print """Content-Type: text/html - - -""" - - -# Set up, when we're invoked as a CGI under a web server -def init_cgi(): - global uname - import Cookie, phpsession - - try: - # Hopefully our PHP session cookie - cookie = Cookie.SimpleCookie(os.environ["HTTP_COOKIE"]) - sessid = cookie["PHPSESSID"].value - if ".." in sessid: - sessid = "XXX" - f = open("/var/lib/php5/sessions/sess_" + sessid, "r") - sess = phpsession.load(f) - uname = sess["webcal_login"] - f.close() - - except: - print """ - Bad Webcalendar Session - - - - Redirecting you to Webcalendar for login... - - - """ - sys.exit(0) - # Set up when we're manually invoked (for testing) def init_cli(): global uname, Target if not os.isatty(0): @@ -175,7 +140,8 @@ if __name__ == "__main__": if len(sys.argv) > 1: init_cli() else: - init_cgi() - run() + uname = auth.init_cgi() + if uname: + run() Index: cgi-bin/webcal.py ================================================================== --- cgi-bin/webcal.py +++ cgi-bin/webcal.py @@ -1,10 +1,10 @@ # # today.py # Pull all events from a icalendar, display today's # -import time, os, calendar +import time, os, calendar, hashlib import MySQLdb as mysql import pdb # Indices into webcal_entry WE_ID = 0 # Entry's ID @@ -447,5 +447,45 @@ ev.color = tup[1] entries.add(ev) c.close() return entries + +# The cookie value is hashed against a value derived from the +# installation password. This table creates the values which +# drive the hash. +Offsets = [] +def md5(s): + m = hashlib.md5(s).digest() + return "".join(("%02x" % (ord(c),)) for c in m) +def init_salt(): + global cfg, Offsets + + salt = cfg.get("install_password") or md5(cfg.get("db_login")) + salt2 = md5(cfg.get("db_password", "oogabooga")) + for i in xrange(max(len(salt), len(salt2))): + val = 0 + if i < len(salt): + val += ord(salt[i]) + if i < len(salt2): + val += ord(salt2[i]) + Offsets.append(val & 0x7F) +init_salt() + +# Decode a string ala PHP's function of the same name +# +# It's salted with the install password, available via the +# system config which we've already pulled in. +def decode_string(encoded): + global Offsets + + ol = len(Offsets) + decoded = "" + idx = 0 + while encoded: + val = ord(encoded[:2].decode("hex")) + encoded = encoded[2:] + val = (((val - Offsets[idx % ol]) + 256) & 0xFF) + decoded += chr(val) + idx += 1 + + return decoded