1 /*
2  * Copyright (C) 2020 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include <sys/stat.h>   // umask
18 #include <sys/types.h>  // umask
19 
20 #include <fstream>
21 #include <memory>
22 #include <ostream>
23 #include <string>
24 #include <vector>
25 
26 #include "Commands.h"
27 #include "android-base/stringprintf.h"
28 #include "idmap2/BinaryStreamVisitor.h"
29 #include "idmap2/CommandLineOptions.h"
30 #include "idmap2/CommandUtils.h"
31 #include "idmap2/FileUtils.h"
32 #include "idmap2/Idmap.h"
33 #include "idmap2/Policies.h"
34 #include "idmap2/PolicyUtils.h"
35 #include "idmap2/SysTrace.h"
36 
37 using android::base::StringPrintf;
38 using android::idmap2::BinaryStreamVisitor;
39 using android::idmap2::CommandLineOptions;
40 using android::idmap2::Error;
41 using android::idmap2::Idmap;
42 using android::idmap2::OverlayResourceContainer;
43 using android::idmap2::Result;
44 using android::idmap2::TargetResourceContainer;
45 using android::idmap2::Unit;
46 using android::idmap2::utils::kIdmapCacheDir;
47 using android::idmap2::utils::kIdmapFilePermissionMask;
48 using android::idmap2::utils::PoliciesToBitmaskResult;
49 using android::idmap2::utils::UidHasWriteAccessToPath;
50 
CreateMultiple(const std::vector<std::string> & args)51 Result<Unit> CreateMultiple(const std::vector<std::string>& args) {
52   SYSTRACE << "CreateMultiple " << args;
53   std::string target_apk_path;
54   std::string idmap_dir = kIdmapCacheDir;
55   std::vector<std::string> overlay_apk_paths;
56   std::vector<std::string> policies;
57   bool ignore_overlayable = false;
58 
59   const CommandLineOptions opts =
60       CommandLineOptions("idmap2 create-multiple")
61           .MandatoryOption("--target-apk-path",
62                            "input: path to apk which will have its resources overlaid",
63                            &target_apk_path)
64           .MandatoryOption("--overlay-apk-path",
65                            "input: path to apk which contains the new resource values",
66                            &overlay_apk_paths)
67           .OptionalOption("--idmap-dir",
68                           StringPrintf("output: path to the directory in which to write idmap file"
69                                        " (defaults to %s)",
70                                        kIdmapCacheDir),
71                           &idmap_dir)
72           .OptionalOption("--policy",
73                           "input: an overlayable policy this overlay fulfills"
74                           " (if none or supplied, the overlay policy will default to \"public\")",
75                           &policies)
76           .OptionalFlag("--ignore-overlayable", "disables overlayable and policy checks",
77                         &ignore_overlayable);
78   const auto opts_ok = opts.Parse(args);
79   if (!opts_ok) {
80     return opts_ok.GetError();
81   }
82 
83   PolicyBitmask fulfilled_policies = 0;
84   auto conv_result = PoliciesToBitmaskResult(policies);
85   if (conv_result) {
86     fulfilled_policies |= *conv_result;
87   } else {
88     return conv_result.GetError();
89   }
90 
91   if (fulfilled_policies == 0) {
92     fulfilled_policies |= PolicyFlags::PUBLIC;
93   }
94 
95   const auto target = TargetResourceContainer::FromPath(target_apk_path);
96   if (!target) {
97     return Error("failed to load target '%s'", target_apk_path.c_str());
98   }
99 
100   std::vector<std::string> idmap_paths;
101   for (const std::string& overlay_apk_path : overlay_apk_paths) {
102     const std::string idmap_path = Idmap::CanonicalIdmapPathFor(idmap_dir, overlay_apk_path);
103     const uid_t uid = getuid();
104     if (!UidHasWriteAccessToPath(uid, idmap_path)) {
105       LOG(WARNING) << "uid " << uid << "does not have write access to " << idmap_path.c_str();
106       continue;
107     }
108 
109     // TODO(b/175014391): Support multiple overlay tags in OverlayConfig
110     if (!Verify(idmap_path, target_apk_path, overlay_apk_path, "", fulfilled_policies,
111                 !ignore_overlayable)) {
112       const auto overlay = OverlayResourceContainer::FromPath(overlay_apk_path);
113       if (!overlay) {
114         LOG(WARNING) << "failed to load apk " << overlay_apk_path.c_str();
115         continue;
116       }
117 
118       const auto idmap =
119           Idmap::FromContainers(**target, **overlay, "", fulfilled_policies, !ignore_overlayable);
120       if (!idmap) {
121         LOG(WARNING) << "failed to create idmap";
122         continue;
123       }
124 
125       umask(kIdmapFilePermissionMask);
126       std::ofstream fout(idmap_path);
127       if (fout.fail()) {
128         LOG(WARNING) << "failed to open idmap path " << idmap_path.c_str();
129         continue;
130       }
131 
132       BinaryStreamVisitor visitor(fout);
133       (*idmap)->accept(&visitor);
134       fout.close();
135       if (fout.fail()) {
136         LOG(WARNING) << "failed to write to idmap path %s" << idmap_path.c_str();
137         continue;
138       }
139     }
140 
141     idmap_paths.emplace_back(idmap_path);
142   }
143 
144   for (const std::string& idmap_path : idmap_paths) {
145     std::cout << idmap_path << std::endl;
146   }
147 
148   return Unit{};
149 }
150