migration from private to rsa
[external/bash.git] / debian / bash.preinst.c
1 /* Copyright (c) 1999 Anthony Towns
2  * Copyright (c) 2000, 2002 Matthias Klose
3  * Copyright (c) 2008 Canonical Ltd
4  *
5  * You may freely use, distribute, and modify this program.
6  */
7
8 // Don't rely on /bin/sh and popen!
9
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 #include <unistd.h>
14 #include <fcntl.h>
15 #include <limits.h>
16 #include <sys/types.h>
17 #include <sys/wait.h>
18
19 /* XXX: evil kludge, deal with arbitrary name lengths */
20 #ifndef PATH_MAX
21 #define PATH_MAX 4096
22 #endif
23
24 #include "md5.h"
25
26 int check_predepends(void)
27 {
28     pid_t child;
29
30     switch(child = fork()) {
31       case -1:
32         /* fork failed */
33         return EXIT_FAILURE;
34
35       case 0:
36         /* i'm the child */
37         {
38             execl( "/usr/bin/dpkg", "/usr/bin/dpkg",
39                    "--assert-support-predepends", NULL );
40            _exit(127);
41         }
42
43       default:
44         /* i'm the parent */
45         {
46             int status;
47             pid_t pid;
48             pid = wait(&status);
49             if (pid == child) {
50                 if (WIFEXITED(status) && WEXITSTATUS(status) == 0) {
51                     return EXIT_SUCCESS;
52                 }
53             }
54         }
55     }
56     return EXIT_FAILURE;
57 }
58
59 #define BUFSIZE 8192
60
61 int md5sum_match(char *fn, char* fn_digest)
62 {
63   md5_state_t md5;
64   md5_byte_t digest[16];
65   unsigned char hexdigest[33];
66   int i, j, fd;
67   size_t nbytes;
68   md5_byte_t buf[BUFSIZE];
69
70   // if not existant, md5sums don't match
71   if (access(fn, R_OK))
72     return 0;
73   if ((fd = open(fn, O_RDONLY)) == -1)
74     return 0;
75   
76   md5_init(&md5);
77   while (nbytes = read(fd, buf, BUFSIZE))
78     md5_append(&md5, buf, nbytes);
79   md5_finish(&md5, digest);
80   close(fd);
81
82   for (i = j = 0; i < 16; i++) {
83     unsigned char c;
84     c = (digest[i] >> 4) & 0xf;
85     c = (c > 9) ? c + 'a' - 10 : c + '0';
86     hexdigest[j++] = c;
87     c = (digest[i] & 0xf);
88     c = (c > 9) ? c + 'a' - 10 : c + '0';
89     hexdigest[j++] = c;
90   }
91   hexdigest[j] = '\0';
92 #ifdef NDEBUG
93   fprintf(stderr, "fn=%s, md5sum=%s, expected=%s\n", fn, hexdigest, fn_digest);
94 #endif
95   return !strcmp(fn_digest, hexdigest);
96 }
97
98 int unmodified_file(char *fn, int md5sumc, unsigned char* md5sumv[])
99 {
100   int i;
101
102   // if not existant, pretend its unmodified
103   if (access(fn, R_OK))
104     return 1;
105   for (i = 0; i < md5sumc; i++) {
106     if (md5sum_match(fn, md5sumv[i]))
107       return 1;
108   }
109   return 0;
110 }
111
112 unsigned char *md5sumv_bash_profile[] = {
113   "d1a8c44e7dd1bed2f3e75d1343b6e4e1", // dapper, edgy, etch
114   "0bc1802860b758732b862ef973cd79ff", // feisty, gutsy
115 };
116 const int md5sumc_bash_profile = sizeof(md5sumv_bash_profile) / sizeof (char *);
117
118 unsigned char *md5sumv_profile[] = {
119   "7d97942254c076a2ea5ea72180266420", // feisty, gutsy
120 };
121 const int md5sumc_profile = sizeof(md5sumv_profile) / sizeof (char *);
122
123 #ifdef BC_CONFIG
124 unsigned char *md5sumv_completion[] = {
125   "2bc0b6cf841eefd31d607e618f1ae29d", // dapper
126   "ae1d298e51ea7f8253eea8b99333561f", // edgy
127   "adc2e9fec28bd2d4a720e725294650f0", // feisty
128   "c8bce25ea68fb0312579a421df99955c", // gutsy, and last one in bash
129   "9da8d1c95748865d516764fb9af82af9", // etch, sid (last one in bash)
130 };
131 const int md5sumc_completion = sizeof(md5sumv_completion) / sizeof (char *);
132 #endif
133
134 char *check_diversion(void)
135 {
136     pid_t child;
137     int pipedes[2];
138
139     if (pipe(pipedes))
140         return NULL;
141
142
143     switch(child = fork()) {
144       case -1:
145         /* fork failed */
146         close(pipedes[0]);
147         close(pipedes[1]);
148         return NULL;
149
150       case 0:
151         /* i'm the child */
152         {
153             if (dup2(pipedes[STDOUT_FILENO], STDOUT_FILENO) < 0)
154                 _exit(127);
155             close(pipedes[STDIN_FILENO]);
156             close(pipedes[STDOUT_FILENO]);
157             execl( "/usr/sbin/dpkg-divert", "/usr/sbin/dpkg-divert",
158                    "--list", "/bin/sh", NULL );
159            _exit(127);
160         }
161
162       default:
163         /* i'm the parent */
164         {
165             static char line[1024];
166             FILE *fd;
167
168             close (pipedes[STDOUT_FILENO]);
169             fcntl (pipedes[STDIN_FILENO], F_SETFD, FD_CLOEXEC);
170             fd = fdopen (pipedes[STDIN_FILENO], "r");
171
172             while (fgets(line, 1024, fd) != NULL) {
173                 line[strlen(line)-1] = '\0';
174                 break;
175             }
176             fclose(fd);
177             return line;
178         }
179     }
180     return NULL;
181 }
182
183 const char *msg =
184   "As bash for Debian is destined to provide a working /bin/sh (pointing to\n"
185   "/bin/bash) your link will be overwritten by a default link.\n\n"
186   "If you don't want further upgrades to overwrite your customization, please\n"
187   "read /usr/share/doc/bash/README.Debian.gz for a more permanent solution.\n\n"
188   "[Press RETURN to continue]";
189
190 int main(void) {
191     int targetlen;
192     char target[PATH_MAX+1], answer[1024], *fn;
193
194     fn = "/etc/skel/.bash_profile";
195     if (!access(fn, R_OK)) {
196       if (unmodified_file(fn, md5sumc_bash_profile, md5sumv_bash_profile)) {
197         printf("removing %s in favour of /etc/skel/.profile\n", fn);
198         unlink(fn);
199       }
200       else {
201         fn = "/etc/skel/.profile";
202         if (!access(fn, R_OK)) {
203           if (unmodified_file(fn, md5sumc_profile, md5sumv_profile)) {
204             printf("renaming /etc/skel/.bash_profile to %s\n", fn);
205             rename("/etc/skel/.bash_profile", fn);
206           }
207         }
208       }
209     }
210
211 #ifdef BC_CONFIG
212     fn = "/etc/bash_completion";
213     if (!access(fn, R_OK)) {
214       if (unmodified_file(fn, md5sumc_completion, md5sumv_completion)) {
215         printf("removing unmodified %s, now in package bash-completion\n", fn);
216         unlink(fn);
217       }
218     }
219 #endif
220
221     if (check_predepends() != EXIT_SUCCESS) {
222         printf("\nPlease upgrade to a new version of dpkg\n\n");
223         return EXIT_FAILURE;
224     }
225     targetlen = readlink("/bin/sh", target, PATH_MAX);
226     if (targetlen == -1) {
227         // error reading link. Will be overwritten.
228         puts("The bash upgrade discovered that something is wrong with your /bin/sh link.");
229         puts(msg);
230         fgets(answer, 1024, stdin);
231         return EXIT_SUCCESS;
232     }
233     target[targetlen] = '\0';
234     if (strcmp(target, "bash") != 0 && strcmp(target, "/bin/bash") != 0) {
235         char *diversion = check_diversion();
236
237         if (diversion == NULL)
238             return EXIT_FAILURE;
239         // printf("diversion: /%s/\n", diversion);
240         if (strcmp(diversion, "") != 0)
241             // link is diverted
242             return EXIT_SUCCESS;
243         printf("The bash upgrade discovered that your /bin/sh link points to %s.\n", target);
244         puts(msg);
245         fgets(answer, 1024, stdin);
246         return EXIT_SUCCESS;
247     }
248
249     return EXIT_SUCCESS;
250 }