This file is indexed.

/usr/share/pyshared/softwareproperties/ppa.py is in python-software-properties 0.82.7.5.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
#  software-properties PPA support
#
#  Copyright (c) 2004-2009 Canonical Ltd.
#
#  Author: Michael Vogt <mvo@debian.org>
#
#  This program is free software; you can redistribute it and/or
#  modify it under the terms of the GNU General Public License as
#  published by the Free Software Foundation; either version 2 of the
#  License, or (at your option) any later version.
#
#  This program is distributed in the hope that it will be useful,
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
#  GNU General Public License for more details.
#
#  You should have received a copy of the GNU General Public License
#  along with this program; if not, write to the Free Software
#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
#  USA

import apt_pkg
import json
import re
import os
import subprocess
import tempfile
import shutil

from threading import Thread
import pycurl

DEFAULT_KEYSERVER = "hkp://keyserver.ubuntu.com:80/"
# maintained until 2015
LAUNCHPAD_PPA_API = 'https://launchpad.net/api/1.0/~%s/+archive/%s'
# None means use pycurl default 
LAUNCHPAD_PPA_CERT = None

def encode(s):
    return re.sub("[^a-zA-Z0-9_-]","_", s)

def expand_ppa_line(abrev, distro_codename):
    """ Convert an abbreviated ppa name of the form 'ppa:$name' to a
        proper sources.list line of the form 'deb ...' """
    # leave non-ppa: lines unchanged
    if not abrev.startswith("ppa:"):
        return (abrev, None)
    # FIXME: add support for dependency PPAs too (once we can get them
    #        via some sort of API, see LP #385129)
    abrev = abrev.split(":")[1]
    ppa_owner = abrev.split("/")[0]
    try:
        ppa_name = abrev.split("/")[1]
    except IndexError, e:
        ppa_name = "ppa"
    sourceslistd = apt_pkg.config.find_dir("Dir::Etc::sourceparts")
    line = "deb http://ppa.launchpad.net/%s/%s/ubuntu %s main" % (
        ppa_owner, ppa_name, distro_codename)
    filename = "%s/%s-%s-%s.list" % (
        sourceslistd, encode(ppa_owner), encode(ppa_name), distro_codename)
    return (line, filename)

class CurlCallback:
    def __init__(self):
        self.contents = ''

    def body_callback(self, buf):
        self.contents = self.contents + buf

def get_ppa_info_from_lp(owner_name, ppa_name):
    lp_url = LAUNCHPAD_PPA_API % (owner_name, ppa_name)
    # we ask for a JSON structure from lp_page, we could use
    # simplejson, but the format is simple enough for the regexp
    callback = CurlCallback()
    curl = pycurl.Curl()
    curl.setopt(pycurl.SSL_VERIFYPEER, 1)
    curl.setopt(pycurl.SSL_VERIFYHOST, 2)
    curl.setopt(pycurl.WRITEFUNCTION, callback.body_callback)
    # only useful for testing
    if LAUNCHPAD_PPA_CERT:
        curl.setopt(pycurl.CAINFO, LAUNCHPAD_PPA_CERT)
    curl.setopt(pycurl.URL, str(lp_url))
    curl.setopt(pycurl.HTTPHEADER, ["Accept: application/json"])
    curl.perform()
    curl.close()
    lp_page = callback.contents
    return json.loads(lp_page)

class AddPPASigningKeyThread(Thread):
    " thread class for adding the signing key in the background "

    def __init__(self, ppa_path, keyserver=None):
        Thread.__init__(self)
        self.ppa_path = ppa_path
        self.keyserver = (keyserver if keyserver is not None
                          else DEFAULT_KEYSERVER)
        
    def run(self):
        self.add_ppa_signing_key(self.ppa_path)
        
    def add_ppa_signing_key(self, ppa_path):
        """Query and add the corresponding PPA signing key.
        
        The signing key fingerprint is obtained from the Launchpad PPA page,
        via a secure channel, so it can be trusted.
        """
        def cleanup(tmpdir):
            import shutil
            shutil.rmtree(tmp_keyring_dir)
        def fail_with_msg(msg, tmpdir=None):
            print msg
            if tmpdir:
                cleanup(tmpdir)
            return False
        owner_name, ppa_name, distro = ppa_path[1:].split('/')
        try:
            ppa_info = get_ppa_info_from_lp(owner_name, ppa_name)
        except pycurl.error as e:
            print "Error reading %s: %s" % (ppa_path, e[1])
            return False
        try:
            signing_key_fingerprint = ppa_info["signing_key_fingerprint"]
        except IndexError as e:
            print "Error: can't find signing_key_fingerprint at %s" % ppa_path
            return False
        # double check that the signing key is a v4 fingerprint (160bit)
        if len(signing_key_fingerprint) < 160/8:
            return fail_with_msg(
                "Error: signing key fingerprint '%s' too short" % 
                signing_key_fingerprint)
        # create a temp keyring dir
        tmp_keyring_dir = tempfile.mkdtemp()
        tmp_secret_keyring = os.path.join(tmp_keyring_dir, "secring.gpg")
        tmp_keyring = os.path.join(tmp_keyring_dir, "pubring.gpg")
        # default options for gpg
        gpg_default_options = [
            "gpg", 
            "--no-default-keyring", "--no-options",
            "--homedir", tmp_keyring_dir,
            ]
        # download the key to a temp keyring first
        res = subprocess.call(gpg_default_options + [
            "--secret-keyring", tmp_secret_keyring,
            "--keyring", tmp_keyring,
            "--keyserver", self.keyserver,
            "--recv", signing_key_fingerprint,
            ])
        if res != 0:
            return fail_with_msg("recv failed", tmp_keyring_dir)
        # now export again using the long key id (to ensure that there is
        # really only this one key in our keyring) and not someone MITM us
        tmp_export_keyring = os.path.join(tmp_keyring_dir, "export-keyring.gpg")
        res = subprocess.call(gpg_default_options + [
            "--keyring", tmp_keyring,
            "--output", tmp_export_keyring,
            "--export", signing_key_fingerprint,
            ])
        if res != 0:
            return fail_with_msg("export failed", tmp_keyring_dir)
        # now verify the fingerprint, this is probably redundant as we
        # exported by the fingerprint in the previous command but its
        # still good paranoia
        output = subprocess.Popen(
            gpg_default_options + [
                "--keyring", tmp_export_keyring,
                "--fingerprint",
                "--batch",
                "--with-colons",
                ], 
            stdout=subprocess.PIPE, 
            universal_newlines=True).communicate()[0]
        got_fingerprint=None
        for line in output.splitlines():
            if line.startswith("fpr:"):
                got_fingerprint = line.split(":")[9]
                # stop after the first to ensure no subkey trickery
                break
        if got_fingerprint != signing_key_fingerprint:
            return fail_with_msg(
                "Fingerprints do not match, not importing: '%s' != '%s'" % (
                    signing_key_fingerprint, got_fingerprint))
        res = subprocess.call(["apt-key", "add", tmp_keyring])
        # cleanup
        cleanup(tmp_keyring_dir)
        return (res == 0)


if __name__ == "__main__":
    import sys
    owner_name, ppa_name = sys.argv[1].split(":")[1].split("/")
    print get_ppa_info_from_lp(owner_name, ppa_name)